Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,873 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
fn (mut t Transformer) make_array_new_call(elem_type string, len_expr flat.NodeId, cap_expr flat.NodeId) flat.NodeId {
|
||||
return t.make_call_typed('array_new', arr3(t.make_sizeof_type(elem_type), len_expr, cap_expr),
|
||||
'[]${elem_type}')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_init_to_runtime(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.value.len == 0 || is_fixed_array_type(node.value) {
|
||||
return id
|
||||
}
|
||||
elem_type := node.value
|
||||
mut len_expr := t.make_int_literal(0)
|
||||
mut cap_expr := t.make_int_literal(0)
|
||||
mut init_expr := flat.empty_node
|
||||
for i in 0 .. node.children_count {
|
||||
child := t.a.child_node(&node, i)
|
||||
if child.kind == .field_init && child.children_count > 0 {
|
||||
val := t.transform_expr(t.a.child(child, 0))
|
||||
if child.value == 'len' {
|
||||
len_expr = val
|
||||
} else if child.value == 'cap' {
|
||||
cap_expr = val
|
||||
} else if child.value == 'init' {
|
||||
init_expr = val
|
||||
}
|
||||
}
|
||||
}
|
||||
new_call := t.make_array_new_call(elem_type, len_expr, cap_expr)
|
||||
if int(init_expr) < 0 {
|
||||
return new_call
|
||||
}
|
||||
tmp_name := t.new_temp('arr_init')
|
||||
idx_name := t.new_temp('arr_idx')
|
||||
t.pending_stmts << t.make_decl_assign_typed(tmp_name, new_call, '[]${elem_type}')
|
||||
init_idx := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(t.make_ident(tmp_name),
|
||||
'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
||||
elem_lhs := t.make_index(t.make_ident(tmp_name), t.make_ident(idx_name), elem_type)
|
||||
assign := t.make_assign(elem_lhs, init_expr)
|
||||
t.pending_stmts << t.make_for_stmt(init_idx, cond, post, arr1(assign), node)
|
||||
return t.make_ident(tmp_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_literal_to_runtime(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
array_type := t.node_type(id)
|
||||
if !array_type.starts_with('[]') {
|
||||
return id
|
||||
}
|
||||
elem_type := array_type[2..]
|
||||
tmp_name := t.new_temp('arr_lit')
|
||||
t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(elem_type,
|
||||
t.make_int_literal(0), t.make_int_literal(node.children_count)), array_type)
|
||||
for i in 0 .. node.children_count {
|
||||
elem_id := t.a.child(&node, i)
|
||||
elem := t.a.nodes[int(elem_id)]
|
||||
if elem.kind == .prefix && elem.value == '...' && elem.children_count > 0 {
|
||||
spread := t.transform_expr(t.a.child(&elem, 0))
|
||||
call := t.make_call_typed('array_push_many', arr2(t.make_prefix(.amp,
|
||||
t.make_ident(tmp_name)), spread), 'void')
|
||||
t.pending_stmts << t.make_expr_stmt(call)
|
||||
continue
|
||||
}
|
||||
value_name := t.new_temp('arr_val')
|
||||
value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {
|
||||
t.wrap_sum_value(elem_id, elem_type)
|
||||
} else {
|
||||
t.transform_expr(elem_id)
|
||||
}
|
||||
t.pending_stmts << t.make_decl_assign_typed(value_name, value, elem_type)
|
||||
call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(tmp_name)), t.make_prefix(.amp,
|
||||
t.make_ident(value_name))), 'void')
|
||||
t.pending_stmts << t.make_expr_stmt(call)
|
||||
}
|
||||
return t.make_ident(tmp_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_array_literal_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
|
||||
array_type := t.normalize_type_alias(target_type)
|
||||
if !array_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := array_type[2..]
|
||||
tmp_name := t.new_temp('arr_lit')
|
||||
t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(elem_type,
|
||||
t.make_int_literal(0), t.make_int_literal(node.children_count)), array_type)
|
||||
for i in 0 .. node.children_count {
|
||||
elem_id := t.a.child(&node, i)
|
||||
elem := t.a.nodes[int(elem_id)]
|
||||
if elem.kind == .prefix && elem.value == '...' && elem.children_count > 0 {
|
||||
spread := t.transform_expr_for_type(t.a.child(&elem, 0), array_type)
|
||||
call := t.make_call_typed('array_push_many', arr2(t.make_prefix(.amp,
|
||||
t.make_ident(tmp_name)), spread), 'void')
|
||||
t.pending_stmts << t.make_expr_stmt(call)
|
||||
continue
|
||||
}
|
||||
value_name := t.new_temp('arr_val')
|
||||
value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {
|
||||
t.wrap_sum_value(elem_id, elem_type)
|
||||
} else {
|
||||
t.transform_expr_for_type(elem_id, elem_type)
|
||||
}
|
||||
t.pending_stmts << t.make_decl_assign_typed(value_name, value, elem_type)
|
||||
call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(tmp_name)), t.make_prefix(.amp,
|
||||
t.make_ident(value_name))), 'void')
|
||||
t.pending_stmts << t.make_expr_stmt(call)
|
||||
}
|
||||
return t.make_ident(tmp_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_empty_array_init_for_type(node flat.Node, target_type string) ?flat.NodeId {
|
||||
if node.value.len > 0 || node.children_count > 0 {
|
||||
return none
|
||||
}
|
||||
array_type := t.normalize_type_alias(target_type)
|
||||
if !array_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := array_type[2..]
|
||||
return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
|
||||
}
|
||||
|
||||
fn (mut t Transformer) try_lower_array_append_stmt(id flat.NodeId) ?[]flat.NodeId {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind != .infix || node.op != .left_shift || node.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
lhs_id := t.a.child(&node, 0)
|
||||
mut lhs_type := t.lvalue_type(lhs_id)
|
||||
lhs_type = t.normalize_type_alias(lhs_type)
|
||||
mut array_type := t.clean_array_append_lhs_type(lhs_type)
|
||||
if !array_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := array_type[2..]
|
||||
rhs_id := t.a.child(&node, 1)
|
||||
mut rhs_type := t.normalize_type_alias(t.node_type(rhs_id))
|
||||
rhs_node := t.a.nodes[int(rhs_id)]
|
||||
mut push_many := t.array_append_rhs_is_push_many(lhs_id, rhs_id, rhs_type, elem_type)
|
||||
if push_many && rhs_node.kind == .array_literal && !rhs_type.starts_with('[]') {
|
||||
t.a.nodes[int(rhs_id)].typ = array_type
|
||||
rhs_type = array_type
|
||||
}
|
||||
|
||||
mut result := []flat.NodeId{}
|
||||
lhs := t.transform_lvalue(lhs_id)
|
||||
t.drain_pending(mut result)
|
||||
mut rhs := if !push_many
|
||||
&& (elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types) {
|
||||
t.wrap_sum_value(rhs_id, elem_type)
|
||||
} else {
|
||||
t.transform_expr(rhs_id)
|
||||
}
|
||||
if !push_many {
|
||||
rhs = t.coerce_transformed_expr_to_type(rhs, rhs_id, elem_type)
|
||||
}
|
||||
t.drain_pending(mut result)
|
||||
if rhs_type.len == 0 {
|
||||
rhs_type = t.node_type(rhs)
|
||||
push_many = t.array_append_rhs_is_push_many(lhs_id, rhs_id, rhs_type, elem_type)
|
||||
}
|
||||
|
||||
lhs_addr := t.runtime_addr(lhs, lhs_type)
|
||||
if push_many {
|
||||
call := if is_fixed_array_type(rhs_type) {
|
||||
t.make_call_typed('array_push_many_ptr', arr3(lhs_addr, rhs,
|
||||
t.make_fixed_array_len_expr(rhs_type)), 'void')
|
||||
} else {
|
||||
t.make_call_typed('array_push_many', arr2(lhs_addr, rhs), 'void')
|
||||
}
|
||||
result << t.make_expr_stmt(call)
|
||||
return result
|
||||
}
|
||||
value_name := t.new_temp('arr_val')
|
||||
result << t.make_decl_assign_typed(value_name, rhs, elem_type)
|
||||
result << t.make_expr_stmt(t.make_call_typed('array_push', arr2(lhs_addr, t.make_prefix(.amp,
|
||||
t.make_ident(value_name))), 'void'))
|
||||
return result
|
||||
}
|
||||
|
||||
fn (t &Transformer) clean_array_append_lhs_type(typ string) string {
|
||||
mut clean := t.normalize_type_alias(typ).trim_space()
|
||||
for {
|
||||
if clean.starts_with('&') {
|
||||
clean = clean[1..].trim_space()
|
||||
continue
|
||||
}
|
||||
if clean.starts_with('shared ') {
|
||||
clean = clean[7..].trim_space()
|
||||
continue
|
||||
}
|
||||
if clean.starts_with('atomic ') {
|
||||
clean = clean[7..].trim_space()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return clean
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_prepend_call(node flat.Node, fn_node flat.Node, base_type string, elem_type string) ?flat.NodeId {
|
||||
if node.children_count < 2 || fn_node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
value_id := t.a.child(&node, 1)
|
||||
base := t.transform_lvalue(base_id)
|
||||
value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {
|
||||
t.wrap_sum_value(value_id, elem_type)
|
||||
} else {
|
||||
t.transform_expr_for_type(value_id, elem_type)
|
||||
}
|
||||
value_name := t.new_temp('arr_val')
|
||||
t.pending_stmts << t.make_decl_assign_typed(value_name, value, elem_type)
|
||||
return t.make_call_typed('array__prepend', arr2(t.runtime_addr(base, base_type), t.make_prefix(.amp,
|
||||
t.make_ident(value_name))), 'void')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_insert_call(node flat.Node, fn_node flat.Node, base_type string, elem_type string) ?flat.NodeId {
|
||||
if node.children_count < 3 || fn_node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
index_id := t.a.child(&node, 1)
|
||||
value_id := t.a.child(&node, 2)
|
||||
base := t.transform_lvalue(base_id)
|
||||
index := t.transform_expr_for_type(index_id, 'int')
|
||||
value := if elem_type in t.sum_types || t.resolve_sum_name(elem_type) in t.sum_types {
|
||||
t.wrap_sum_value(value_id, elem_type)
|
||||
} else {
|
||||
t.transform_expr_for_type(value_id, elem_type)
|
||||
}
|
||||
value_name := t.new_temp('arr_val')
|
||||
t.pending_stmts << t.make_decl_assign_typed(value_name, value, elem_type)
|
||||
return t.make_call_typed('array__insert', arr3(t.runtime_addr(base, base_type), index, t.make_prefix(.amp,
|
||||
t.make_ident(value_name))), 'void')
|
||||
}
|
||||
|
||||
fn (t &Transformer) array_append_rhs_is_push_many(lhs_id flat.NodeId, rhs_id flat.NodeId, rhs_type string, elem_type string) bool {
|
||||
clean_rhs_type := rhs_type.trim_space()
|
||||
if clean_rhs_type.starts_with('[]') {
|
||||
if t.array_append_elem_types_match(clean_rhs_type[2..], elem_type) {
|
||||
return true
|
||||
}
|
||||
if declared_rhs_type := t.array_append_ident_type(rhs_id) {
|
||||
if declared_rhs_type.starts_with('[]') {
|
||||
return t.array_append_elem_types_match(declared_rhs_type[2..], elem_type)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if is_fixed_array_type(clean_rhs_type) {
|
||||
return t.array_append_elem_types_match(fixed_array_elem_type(clean_rhs_type), elem_type)
|
||||
}
|
||||
if !isnil(t.tc) {
|
||||
if rhs_resolved := t.tc.expr_type(rhs_id) {
|
||||
rhs_clean := types.unwrap_pointer(rhs_resolved)
|
||||
if rhs_clean is types.Array {
|
||||
return t.array_append_elem_types_match(rhs_clean.elem_type.name(), elem_type)
|
||||
}
|
||||
if rhs_clean is types.ArrayFixed {
|
||||
return t.array_append_elem_types_match(rhs_clean.elem_type.name(), elem_type)
|
||||
}
|
||||
}
|
||||
if lhs_resolved := t.tc.expr_type(lhs_id) {
|
||||
lhs_clean := types.unwrap_pointer(lhs_resolved)
|
||||
if lhs_clean is types.Array && clean_rhs_type in ['array', 'Array'] {
|
||||
return t.tc.c_type(lhs_clean.elem_type) == 'void*'
|
||||
}
|
||||
}
|
||||
}
|
||||
if clean_rhs_type in ['array', 'Array'] {
|
||||
return t.array_append_elem_c_type(elem_type) !in ['array', 'Array']
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (t &Transformer) array_append_elem_types_match(rhs_elem_type string, lhs_elem_type string) bool {
|
||||
rhs_raw := rhs_elem_type.trim_space()
|
||||
lhs_raw := lhs_elem_type.trim_space()
|
||||
if rhs_raw == lhs_raw {
|
||||
return true
|
||||
}
|
||||
rhs_clean := t.normalize_type_alias(rhs_elem_type)
|
||||
lhs_clean := t.normalize_type_alias(lhs_elem_type)
|
||||
if rhs_clean == lhs_clean {
|
||||
return true
|
||||
}
|
||||
if isnil(t.tc) {
|
||||
return false
|
||||
}
|
||||
return t.array_append_elem_c_type(rhs_clean) == t.array_append_elem_c_type(lhs_clean)
|
||||
}
|
||||
|
||||
fn (t &Transformer) array_append_ident_type(id flat.NodeId) ?string {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind != .ident || node.value.len == 0 {
|
||||
return none
|
||||
}
|
||||
typ := t.var_type(node.value)
|
||||
if typ.len == 0 {
|
||||
return none
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (t &Transformer) array_append_elem_c_type(typ string) string {
|
||||
if isnil(t.tc) {
|
||||
return typ
|
||||
}
|
||||
clean := typ.trim_space()
|
||||
if clean.len == 0 {
|
||||
return clean
|
||||
}
|
||||
if !clean.contains('.') {
|
||||
for alias, target in t.tc.type_aliases {
|
||||
if alias.all_after_last('.') == clean {
|
||||
return t.tc.c_type(t.tc.parse_type(target))
|
||||
}
|
||||
}
|
||||
}
|
||||
return t.tc.c_type(t.tc.parse_type(clean))
|
||||
}
|
||||
|
||||
fn (mut t Transformer) array_get_value(base flat.NodeId, index flat.NodeId, elem_type string) flat.NodeId {
|
||||
get_call := t.make_call_typed('array_get', arr2(base, index), 'voidptr')
|
||||
ptr := t.make_cast('&${elem_type}', get_call, '&${elem_type}')
|
||||
value := t.make_prefix(.mul, ptr)
|
||||
t.a.nodes[int(value)].typ = elem_type
|
||||
return value
|
||||
}
|
||||
|
||||
fn (mut t Transformer) array_get_ptr(base flat.NodeId, index flat.NodeId, elem_type string) flat.NodeId {
|
||||
get_call := t.make_call_typed('array_get', arr2(base, index), 'voidptr')
|
||||
return t.make_cast('&${elem_type}', get_call, '&${elem_type}')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_filter_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if node.children_count < 2 || !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := base_type[2..]
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
base := t.stable_expr_for_reuse(base_id)
|
||||
mut prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut prefix)
|
||||
out_name := t.new_temp('filter')
|
||||
idx_name := t.new_temp('filter_idx')
|
||||
prefix << t.make_decl_assign_typed(out_name, t.make_array_new_call(elem_type,
|
||||
t.make_int_literal(0), t.make_selector(base, 'len', 'int')), base_type)
|
||||
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
||||
elem_name_default := t.new_temp('filter_it')
|
||||
elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
|
||||
predicate_id := t.a.child(&node, 1)
|
||||
predicate_node := t.a.nodes[int(predicate_id)]
|
||||
mut predicate_expr_id := predicate_id
|
||||
mut lambda_param := ''
|
||||
if predicate_node.kind == .lambda_expr && predicate_node.children_count > 0 {
|
||||
predicate_expr_id = t.a.child(&predicate_node, predicate_node.children_count - 1)
|
||||
if predicate_node.children_count > 1 {
|
||||
param := t.a.child_node(&predicate_node, 0)
|
||||
if param.kind == .ident && param.value.len > 0 {
|
||||
lambda_param = param.value
|
||||
}
|
||||
}
|
||||
}
|
||||
elem_name := if lambda_param.len > 0 { lambda_param } else { elem_name_default }
|
||||
elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)
|
||||
old_elem := t.var_type(elem_name)
|
||||
t.set_var_type(elem_name, elem_type)
|
||||
predicate_source := if lambda_param.len > 0 {
|
||||
predicate_expr_id
|
||||
} else {
|
||||
t.substitute_ident(predicate_expr_id, 'it', elem_name)
|
||||
}
|
||||
predicate := t.transform_expr(predicate_source)
|
||||
if old_elem.len > 0 {
|
||||
t.set_var_type(elem_name, old_elem)
|
||||
} else {
|
||||
t.unset_var_type(elem_name)
|
||||
}
|
||||
mut loop_body := []flat.NodeId{}
|
||||
loop_body << elem_decl
|
||||
t.drain_pending(mut loop_body)
|
||||
push_call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(out_name)), t.make_prefix(.amp,
|
||||
t.make_ident(elem_name))), 'void')
|
||||
then_block := t.make_block(arr1(t.make_expr_stmt(push_call)))
|
||||
loop_body << t.make_if(predicate, then_block, t.make_empty())
|
||||
prefix << t.make_for_stmt(init, cond, post, loop_body, node)
|
||||
for stmt in prefix {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
return t.make_ident(out_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_map_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if node.children_count < 2 || !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := base_type[2..]
|
||||
map_expr_id := t.a.child(&node, 1)
|
||||
map_expr := t.a.nodes[int(map_expr_id)]
|
||||
mut map_fn_name := ''
|
||||
elem_name := t.new_temp('map_it')
|
||||
old_elem := t.var_type(elem_name)
|
||||
t.set_var_type(elem_name, elem_type)
|
||||
mapped_source := t.substitute_ident(map_expr_id, 'it', elem_name)
|
||||
mut result_elem_type := t.node_type(map_expr_id)
|
||||
mut direct_selector_type := ''
|
||||
mapped_source_node := t.a.nodes[int(mapped_source)]
|
||||
if mapped_source_node.kind == .selector {
|
||||
selector_type := t.resolve_selector_type(mapped_source_node)
|
||||
if selector_type.len > 0 {
|
||||
direct_selector_type = selector_type
|
||||
result_elem_type = selector_type
|
||||
}
|
||||
}
|
||||
if map_expr.kind == .ident {
|
||||
if fn_name := t.resolve_fn_value_ident(map_expr.value) {
|
||||
map_fn_name = fn_name
|
||||
if ret := t.fn_ret_types[fn_name] {
|
||||
result_elem_type = ret
|
||||
} else if !isnil(t.tc) {
|
||||
if ret_type := t.tc.fn_ret_types[fn_name] {
|
||||
result_elem_type = t.normalize_type_alias(ret_type.name())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if result_elem_type.len == 0 {
|
||||
result_elem_type = t.reliable_stringify_type(map_expr_id)
|
||||
}
|
||||
saved_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
mapped_expr := if map_fn_name.len > 0 {
|
||||
t.make_call_typed(map_fn_name, arr1(t.make_ident(elem_name)), result_elem_type)
|
||||
} else {
|
||||
t.transform_expr(mapped_source)
|
||||
}
|
||||
mapped_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts = saved_pending
|
||||
if old_elem.len > 0 {
|
||||
t.set_var_type(elem_name, old_elem)
|
||||
} else {
|
||||
t.unset_var_type(elem_name)
|
||||
}
|
||||
mapped_type := t.node_type(mapped_expr)
|
||||
if mapped_type.len > 0 && mapped_type != 'unknown' {
|
||||
result_elem_type = mapped_type
|
||||
}
|
||||
if direct_selector_type.len > 0 {
|
||||
result_elem_type = direct_selector_type
|
||||
}
|
||||
if mapped_expr_node := t.selector_expr_node(mapped_expr) {
|
||||
selector_type := t.resolve_selector_type(mapped_expr_node)
|
||||
if selector_type.len > 0 {
|
||||
result_elem_type = selector_type
|
||||
}
|
||||
}
|
||||
if result_elem_type.len == 0 {
|
||||
result_elem_type = elem_type
|
||||
}
|
||||
out_type := '[]${result_elem_type}'
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
base := t.stable_expr_for_reuse(base_id)
|
||||
mut prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut prefix)
|
||||
out_name := t.new_temp('map')
|
||||
idx_name := t.new_temp('map_idx')
|
||||
prefix << t.make_decl_assign_typed(out_name, t.make_array_new_call(result_elem_type,
|
||||
t.make_int_literal(0), t.make_selector(base, 'len', 'int')), out_type)
|
||||
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
||||
elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
|
||||
elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)
|
||||
mut loop_body := []flat.NodeId{}
|
||||
loop_body << elem_decl
|
||||
for stmt in mapped_pending {
|
||||
loop_body << stmt
|
||||
}
|
||||
value_name := t.new_temp('map_val')
|
||||
loop_body << t.make_decl_assign_typed(value_name, mapped_expr, result_elem_type)
|
||||
loop_body << t.make_expr_stmt(t.make_call_typed('array_push', arr2(t.make_prefix(.amp,
|
||||
t.make_ident(out_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void'))
|
||||
prefix << t.make_for_stmt(init, cond, post, loop_body, node)
|
||||
for stmt in prefix {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
result := t.make_ident(out_name)
|
||||
t.a.nodes[int(result)].typ = out_type
|
||||
return result
|
||||
}
|
||||
|
||||
fn (t &Transformer) selector_expr_node(id flat.NodeId) ?flat.Node {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .selector {
|
||||
return node
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut t Transformer) substitute_ident(id flat.NodeId, name string, replacement string) flat.NodeId {
|
||||
if int(id) < 0 || name.len == 0 || replacement.len == 0 || name == replacement {
|
||||
return id
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .ident && node.value == name {
|
||||
new_id := t.make_ident(replacement)
|
||||
t.a.nodes[int(new_id)].typ = node.typ
|
||||
return new_id
|
||||
}
|
||||
if node.kind == .lambda_expr && node.children_count > 1 {
|
||||
first := t.a.child_node(&node, 0)
|
||||
if first.kind == .ident && first.value == name {
|
||||
return id
|
||||
}
|
||||
}
|
||||
if node.kind == .call && node.children_count > 1 {
|
||||
fn_id := t.a.child(&node, 0)
|
||||
fn_node := t.a.nodes[int(fn_id)]
|
||||
if fn_node.kind == .selector && fn_node.value in ['any', 'all', 'count', 'filter', 'map'] {
|
||||
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
||||
new_children << t.substitute_ident(fn_id, name, replacement)
|
||||
for i in 1 .. node.children_count {
|
||||
new_children << t.a.child(&node, i)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for child in new_children {
|
||||
t.a.children << child
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: node.kind
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(new_children.len)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
}
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
||||
for i in 0 .. node.children_count {
|
||||
new_children << t.substitute_ident(t.a.child(&node, i), name, replacement)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for child in new_children {
|
||||
t.a.children << child
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: node.kind
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(new_children.len)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_count_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if node.children_count < 2 || !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := base_type[2..]
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
base := t.stable_expr_for_reuse(base_id)
|
||||
mut prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut prefix)
|
||||
result_name := t.new_temp('count')
|
||||
idx_name := t.new_temp('count_idx')
|
||||
prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(0), 'int')
|
||||
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
||||
elem_name := t.new_temp('count_it')
|
||||
elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
|
||||
elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)
|
||||
predicate_id := t.a.child(&node, 1)
|
||||
old_elem := t.var_type(elem_name)
|
||||
t.set_var_type(elem_name, elem_type)
|
||||
predicate := t.transform_expr(t.substitute_ident(predicate_id, 'it', elem_name))
|
||||
if old_elem.len > 0 {
|
||||
t.set_var_type(elem_name, old_elem)
|
||||
} else {
|
||||
t.unset_var_type(elem_name)
|
||||
}
|
||||
mut loop_body := []flat.NodeId{}
|
||||
loop_body << elem_decl
|
||||
t.drain_pending(mut loop_body)
|
||||
inc := t.make_assign_op(t.make_ident(result_name), t.make_int_literal(1), .plus_assign)
|
||||
loop_body << t.make_if(predicate, t.make_block(arr1(inc)), t.make_empty())
|
||||
prefix << t.make_for_stmt(init, cond, post, loop_body, node)
|
||||
for stmt in prefix {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
return t.make_ident(result_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_any_all_call(node flat.Node, fn_node flat.Node, base_type string, method string) ?flat.NodeId {
|
||||
if node.children_count < 2 || !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
elem_type := base_type[2..]
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
base := t.stable_expr_for_reuse(base_id)
|
||||
mut prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut prefix)
|
||||
result_name := t.new_temp(method)
|
||||
idx_name := t.new_temp('${method}_idx')
|
||||
default_value := if method == 'all' {
|
||||
t.make_bool_literal(true)
|
||||
} else {
|
||||
t.make_bool_literal(false)
|
||||
}
|
||||
prefix << t.make_decl_assign_typed(result_name, default_value, 'bool')
|
||||
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
||||
elem_name := t.new_temp('${method}_it')
|
||||
elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
|
||||
elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)
|
||||
predicate_id := t.a.child(&node, 1)
|
||||
old_elem := t.var_type(elem_name)
|
||||
t.set_var_type(elem_name, elem_type)
|
||||
predicate := t.transform_expr(t.substitute_ident(predicate_id, 'it', elem_name))
|
||||
if old_elem.len > 0 {
|
||||
t.set_var_type(elem_name, old_elem)
|
||||
} else {
|
||||
t.unset_var_type(elem_name)
|
||||
}
|
||||
mut loop_body := []flat.NodeId{}
|
||||
loop_body << elem_decl
|
||||
t.drain_pending(mut loop_body)
|
||||
if method == 'all' {
|
||||
not_predicate := t.make_prefix(.not, predicate)
|
||||
assign_false := t.make_assign(t.make_ident(result_name), t.make_bool_literal(false))
|
||||
loop_body << t.make_if(not_predicate, t.make_block(arr1(assign_false)), t.make_empty())
|
||||
} else {
|
||||
assign_true := t.make_assign(t.make_ident(result_name), t.make_bool_literal(true))
|
||||
loop_body << t.make_if(predicate, t.make_block(arr1(assign_true)), t.make_empty())
|
||||
}
|
||||
prefix << t.make_for_stmt(init, cond, post, loop_body, node)
|
||||
for stmt in prefix {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
return t.make_ident(result_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_sort_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if !base_type.starts_with('[]') && !(base_type.starts_with('&')
|
||||
&& base_type[1..].starts_with('[]')) {
|
||||
return none
|
||||
}
|
||||
if node.children_count > 2 {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
base := t.transform_lvalue(base_id)
|
||||
clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
||||
elem_type := clean_type[2..]
|
||||
cmp_id := if node.children_count > 1 { t.a.child(&node, 1) } else { flat.empty_node }
|
||||
t.pending_stmts << t.make_array_default_sort_stmt(base, elem_type, node, cmp_id)
|
||||
return t.make_empty()
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_sorted_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if node.children_count > 2 || !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
clone_name := t.new_temp('sorted')
|
||||
clone_call := t.make_call_typed('array_clone', arr1(t.transform_expr(base_id)), base_type)
|
||||
t.pending_stmts << t.make_decl_assign_typed(clone_name, clone_call, base_type)
|
||||
cmp_id := if node.children_count > 1 { t.a.child(&node, 1) } else { flat.empty_node }
|
||||
t.pending_stmts << t.make_array_default_sort_stmt(t.make_ident(clone_name), base_type[2..],
|
||||
node, cmp_id)
|
||||
return t.make_ident(clone_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_sort_with_compare_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if node.children_count != 2
|
||||
|| (!base_type.starts_with('[]') && !(base_type.starts_with('&')
|
||||
&& base_type[1..].starts_with('[]'))) {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
base := t.transform_lvalue(base_id)
|
||||
clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
||||
elem_type := clean_type[2..]
|
||||
cmp := t.stable_array_compare_fn(t.a.child(&node, 1), elem_type)
|
||||
t.pending_stmts << t.make_array_compare_sort_stmt(base, elem_type, node, cmp)
|
||||
return t.make_empty()
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_array_sorted_with_compare_call(node flat.Node, fn_node flat.Node, base_type string) ?flat.NodeId {
|
||||
if node.children_count != 2 || !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&fn_node, 0)
|
||||
clone_name := t.new_temp('sorted')
|
||||
clone_call := t.make_call_typed('array_clone', arr1(t.transform_expr(base_id)), base_type)
|
||||
t.pending_stmts << t.make_decl_assign_typed(clone_name, clone_call, base_type)
|
||||
elem_type := base_type[2..]
|
||||
cmp := t.stable_array_compare_fn(t.a.child(&node, 1), elem_type)
|
||||
t.pending_stmts << t.make_array_compare_sort_stmt(t.make_ident(clone_name), elem_type, node, cmp)
|
||||
return t.make_ident(clone_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) stable_array_compare_fn(cmp_id flat.NodeId, elem_type string) flat.NodeId {
|
||||
cmp := t.transform_expr(cmp_id)
|
||||
cmp_type := 'fn (&${elem_type}, &${elem_type}) int'
|
||||
return t.stable_transformed_expr_for_reuse(cmp, cmp_type, 'sort_cmp')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_array_default_sort_stmt(base flat.NodeId, elem_type string, src flat.Node, cmp_id flat.NodeId) flat.NodeId {
|
||||
i_name := t.new_temp('sort_i')
|
||||
j_name := t.new_temp('sort_j')
|
||||
tmp_name := t.new_temp('sort_tmp')
|
||||
t.set_var_type(i_name, 'int')
|
||||
t.set_var_type(j_name, 'int')
|
||||
t.set_var_type(tmp_name, elem_type)
|
||||
init := t.make_decl_assign_typed(i_name, t.make_int_literal(1), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(i_name), t.make_selector(base, 'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(i_name), .inc))
|
||||
j_decl := t.make_decl_assign_typed(j_name, t.make_ident(i_name), 'int')
|
||||
inner_cond := t.make_infix(.logical_and, t.make_infix(.gt, t.make_ident(j_name),
|
||||
t.make_int_literal(0)), t.array_sort_less_expr(base, elem_type, j_name, cmp_id))
|
||||
tmp_decl := t.make_decl_assign_typed(tmp_name, t.make_index(base, t.make_ident(j_name),
|
||||
elem_type), elem_type)
|
||||
prev_idx := t.make_infix(.minus, t.make_ident(j_name), t.make_int_literal(1))
|
||||
assign_cur := t.make_index_assign(t.make_index(base, t.make_ident(j_name), elem_type), t.make_index(base,
|
||||
prev_idx, elem_type))
|
||||
prev_idx2 := t.make_infix(.minus, t.make_ident(j_name), t.make_int_literal(1))
|
||||
assign_prev := t.make_index_assign(t.make_index(base, prev_idx2, elem_type),
|
||||
t.make_ident(tmp_name))
|
||||
dec_j := t.make_expr_stmt(t.make_postfix(t.make_ident(j_name), .dec))
|
||||
inner_body := [tmp_decl, assign_cur, assign_prev, dec_j]
|
||||
inner_for := t.make_for_stmt(t.make_empty(), inner_cond, t.make_empty(), inner_body, src)
|
||||
return t.make_for_stmt(init, cond, post, [j_decl, inner_for], src)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_array_compare_sort_stmt(base flat.NodeId, elem_type string, src flat.Node, cmp flat.NodeId) flat.NodeId {
|
||||
i_name := t.new_temp('sort_i')
|
||||
j_name := t.new_temp('sort_j')
|
||||
tmp_name := t.new_temp('sort_tmp')
|
||||
t.set_var_type(i_name, 'int')
|
||||
t.set_var_type(j_name, 'int')
|
||||
t.set_var_type(tmp_name, elem_type)
|
||||
init := t.make_decl_assign_typed(i_name, t.make_int_literal(1), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(i_name), t.make_selector(base, 'len', 'int'))
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(i_name), .inc))
|
||||
j_decl := t.make_decl_assign_typed(j_name, t.make_ident(i_name), 'int')
|
||||
inner_cond := t.make_infix(.logical_and, t.make_infix(.gt, t.make_ident(j_name),
|
||||
t.make_int_literal(0)), t.array_sort_compare_less_expr(base, elem_type, j_name, cmp))
|
||||
tmp_decl := t.make_decl_assign_typed(tmp_name, t.make_index(base, t.make_ident(j_name),
|
||||
elem_type), elem_type)
|
||||
prev_idx := t.make_infix(.minus, t.make_ident(j_name), t.make_int_literal(1))
|
||||
assign_cur := t.make_index_assign(t.make_index(base, t.make_ident(j_name), elem_type), t.make_index(base,
|
||||
prev_idx, elem_type))
|
||||
prev_idx2 := t.make_infix(.minus, t.make_ident(j_name), t.make_int_literal(1))
|
||||
assign_prev := t.make_index_assign(t.make_index(base, prev_idx2, elem_type),
|
||||
t.make_ident(tmp_name))
|
||||
dec_j := t.make_expr_stmt(t.make_postfix(t.make_ident(j_name), .dec))
|
||||
inner_body := [tmp_decl, assign_cur, assign_prev, dec_j]
|
||||
inner_for := t.make_for_stmt(t.make_empty(), inner_cond, t.make_empty(), inner_body, src)
|
||||
return t.make_for_stmt(init, cond, post, [j_decl, inner_for], src)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) array_sort_less_expr(base flat.NodeId, elem_type string, idx_name string, cmp_id flat.NodeId) flat.NodeId {
|
||||
cur := t.make_index(base, t.make_ident(idx_name), elem_type)
|
||||
prev := t.make_index(base, t.make_infix(.minus, t.make_ident(idx_name), t.make_int_literal(1)),
|
||||
elem_type)
|
||||
if int(cmp_id) >= 0 {
|
||||
old_a := t.var_type('a')
|
||||
old_b := t.var_type('b')
|
||||
t.set_var_type('a', elem_type)
|
||||
t.set_var_type('b', elem_type)
|
||||
raw_cmp := t.substitute_array_sort_vars(cmp_id, cur, prev)
|
||||
cmp := t.transform_expr(raw_cmp)
|
||||
if old_a.len > 0 {
|
||||
t.set_var_type('a', old_a)
|
||||
} else {
|
||||
t.unset_var_type('a')
|
||||
}
|
||||
if old_b.len > 0 {
|
||||
t.set_var_type('b', old_b)
|
||||
} else {
|
||||
t.unset_var_type('b')
|
||||
}
|
||||
return cmp
|
||||
}
|
||||
if elem_type == 'string' {
|
||||
return t.make_call_typed('string__lt', arr2(cur, prev), 'bool')
|
||||
}
|
||||
return t.make_infix(.lt, cur, prev)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) array_sort_compare_less_expr(base flat.NodeId, elem_type string, idx_name string, cmp flat.NodeId) flat.NodeId {
|
||||
cur := t.make_index(base, t.make_ident(idx_name), elem_type)
|
||||
prev := t.make_index(base, t.make_infix(.minus, t.make_ident(idx_name), t.make_int_literal(1)),
|
||||
elem_type)
|
||||
call := t.make_call_expr_typed(cmp, arr2(t.make_prefix(.amp, cur), t.make_prefix(.amp, prev)),
|
||||
'int')
|
||||
return t.make_infix(.lt, call, t.make_int_literal(0))
|
||||
}
|
||||
|
||||
fn (mut t Transformer) substitute_array_sort_vars(id flat.NodeId, a_expr flat.NodeId, b_expr flat.NodeId) flat.NodeId {
|
||||
if int(id) < 0 {
|
||||
return id
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .ident {
|
||||
if node.value == 'a' {
|
||||
return a_expr
|
||||
}
|
||||
if node.value == 'b' {
|
||||
return b_expr
|
||||
}
|
||||
return id
|
||||
}
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut children := []flat.NodeId{cap: int(node.children_count)}
|
||||
for i in 0 .. node.children_count {
|
||||
children << t.substitute_array_sort_vars(t.a.child(&node, i), a_expr, b_expr)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for child in children {
|
||||
t.a.children << child
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: node.kind
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: node.children_count
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_index_assign(lhs flat.NodeId, rhs flat.NodeId) flat.NodeId {
|
||||
start := t.a.children.len
|
||||
t.a.children << lhs
|
||||
t.a.children << rhs
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .index_assign
|
||||
op: .assign
|
||||
children_start: start
|
||||
children_count: 2
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
module transform
|
||||
|
||||
import os
|
||||
|
||||
fn (t &Transformer) vmod_root() string {
|
||||
mut dir := if t.cur_file.len > 0 { os.dir(t.cur_file) } else { os.getwd() }
|
||||
if dir.len == 0 {
|
||||
dir = os.getwd()
|
||||
}
|
||||
for {
|
||||
if os.exists(os.join_path(dir, 'v.mod')) {
|
||||
return dir
|
||||
}
|
||||
parent := os.dir(dir)
|
||||
if parent == dir || parent.len == 0 {
|
||||
return if t.cur_file.len > 0 { os.dir(t.cur_file) } else { os.getwd() }
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
return dir
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,414 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
fn (mut t Transformer) transform_for_body(id flat.NodeId, node flat.Node) []flat.NodeId {
|
||||
if node.children_count < 3 {
|
||||
return arr1(id)
|
||||
}
|
||||
// child 0: init statement
|
||||
init_id := t.a.child(&node, 0)
|
||||
mut new_init := init_id
|
||||
if int(init_id) >= 0 {
|
||||
expanded := t.transform_stmt(init_id)
|
||||
if expanded.len > 0 {
|
||||
new_init = expanded[0]
|
||||
}
|
||||
}
|
||||
// child 1: condition expression
|
||||
cond_id := t.a.child(&node, 1)
|
||||
cond_smartcasts := t.extract_all_is_exprs(cond_id)
|
||||
new_cond := t.transform_and_chain_smartcasts(cond_id)
|
||||
mut cond_prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut cond_prefix)
|
||||
// child 2: post statement
|
||||
post_id := t.a.child(&node, 2)
|
||||
mut new_post := post_id
|
||||
if int(post_id) >= 0 {
|
||||
expanded := t.transform_stmt(post_id)
|
||||
if expanded.len > 0 {
|
||||
new_post = expanded[0]
|
||||
}
|
||||
}
|
||||
// children 3..n: body statements
|
||||
mut body_ids := []flat.NodeId{}
|
||||
for i in 3 .. node.children_count {
|
||||
body_ids << t.a.child(&node, i)
|
||||
}
|
||||
for info in cond_smartcasts {
|
||||
t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
|
||||
}
|
||||
new_body := t.transform_stmts(body_ids)
|
||||
for _ in cond_smartcasts {
|
||||
t.pop_smartcast()
|
||||
}
|
||||
mut loop_cond := new_cond
|
||||
mut loop_body := new_body.clone()
|
||||
if cond_prefix.len > 0 {
|
||||
mut guarded_body := []flat.NodeId{cap: cond_prefix.len + new_body.len + 1}
|
||||
for stmt in cond_prefix {
|
||||
guarded_body << stmt
|
||||
}
|
||||
not_cond := t.make_prefix(.not, t.make_paren(new_cond))
|
||||
break_block := t.make_block(arr1(t.a.add(.break_stmt)))
|
||||
guarded_body << t.make_if(not_cond, break_block, t.make_block([]flat.NodeId{}))
|
||||
for stmt in new_body {
|
||||
guarded_body << stmt
|
||||
}
|
||||
loop_cond = t.make_bool_literal(true)
|
||||
loop_body = guarded_body.clone()
|
||||
}
|
||||
// Rebuild the for_stmt with transformed children
|
||||
start := t.a.children.len
|
||||
t.a.children << new_init
|
||||
t.a.children << loop_cond
|
||||
t.a.children << new_post
|
||||
for bid in loop_body {
|
||||
t.a.children << bid
|
||||
}
|
||||
count := t.a.children.len - start
|
||||
new_id := t.a.add_node(flat.Node{
|
||||
kind: .for_stmt
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(count)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
return arr1(new_id)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_for_in_body(id flat.NodeId, node flat.Node) []flat.NodeId {
|
||||
header_count := node.value.int()
|
||||
if header_count < 3 || node.children_count < 3 {
|
||||
return arr1(id)
|
||||
}
|
||||
key_id := t.a.child(&node, 0) // loop var ident — pass through (do not transform a binding)
|
||||
val_id := t.a.child(&node, 1) // may be flat.empty_node (-1)
|
||||
container_id := t.a.child(&node, 2)
|
||||
iter_type := t.detect_for_in_type(node)
|
||||
has_index := int(val_id) >= 0
|
||||
container_is_range := if int(container_id) >= 0 {
|
||||
t.a.nodes[int(container_id)].kind == .range
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if header_count == 4 {
|
||||
range_end_id := t.a.child(&node, 3)
|
||||
body_ids := t.a.children_of(&node)[header_count..].clone()
|
||||
return t.lower_range_for_in(id, node, key_id, container_id, range_end_id, body_ids)
|
||||
}
|
||||
if container_is_range {
|
||||
range_node := t.a.nodes[int(container_id)]
|
||||
if range_node.children_count >= 2 {
|
||||
body_ids := t.a.children_of(&node)[header_count..].clone()
|
||||
return t.lower_range_for_in(id, node, key_id, t.a.child(&range_node, 0), t.a.child(&range_node,
|
||||
1), body_ids)
|
||||
}
|
||||
}
|
||||
if iter_type.starts_with('map[') {
|
||||
return t.rebuild_for_in_stmt(id, node)
|
||||
}
|
||||
if pool_iter := t.pool_get_results_iter_type(container_id) {
|
||||
body_ids := t.a.children_of(&node)[header_count..].clone()
|
||||
return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, pool_iter, has_index,
|
||||
body_ids)
|
||||
}
|
||||
effective_iter := if iter_type.starts_with('...') {
|
||||
'[]' + iter_type[3..]
|
||||
} else if iter_type.starts_with('&[]') {
|
||||
iter_type[1..]
|
||||
} else {
|
||||
iter_type
|
||||
}
|
||||
if effective_iter.starts_with('[]') || effective_iter == 'string'
|
||||
|| is_fixed_array_type(effective_iter) {
|
||||
body_ids := t.a.children_of(&node)[header_count..].clone()
|
||||
return t.lower_indexed_for_in(id, node, key_id, val_id, container_id, effective_iter,
|
||||
has_index, body_ids)
|
||||
}
|
||||
return t.rebuild_for_in_stmt(id, node)
|
||||
}
|
||||
|
||||
fn (t &Transformer) pool_get_results_iter_type(container_id flat.NodeId) ?string {
|
||||
if int(container_id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(container_id)]
|
||||
if node.kind != .call || node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
callee_id := t.a.child(&node, 0)
|
||||
if int(callee_id) < 0 {
|
||||
return none
|
||||
}
|
||||
callee := t.a.nodes[int(callee_id)]
|
||||
if callee.kind != .index || callee.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
base := t.a.child_node(&callee, 0)
|
||||
if base.kind != .selector || base.value !in ['get_results', 'get_results_ref'] {
|
||||
return none
|
||||
}
|
||||
elem_type := t.generic_call_type_arg_name(t.a.child(&callee, 1))
|
||||
if elem_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
if base.value == 'get_results_ref' {
|
||||
return '[]&${elem_type}'
|
||||
}
|
||||
return '[]${elem_type}'
|
||||
}
|
||||
|
||||
fn (mut t Transformer) rebuild_for_in_stmt(_id flat.NodeId, node flat.Node) []flat.NodeId {
|
||||
header_count := node.value.int()
|
||||
key_id := t.a.child(&node, 0)
|
||||
val_id := t.a.child(&node, 1)
|
||||
container_id := t.a.child(&node, 2)
|
||||
new_container := t.transform_expr(container_id)
|
||||
iter_type := t.detect_for_in_type(node)
|
||||
has_index := int(val_id) >= 0
|
||||
container_is_range := if int(container_id) >= 0 {
|
||||
t.a.nodes[int(container_id)].kind == .range
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if header_count == 4 || container_is_range {
|
||||
// range `for i in 0 .. n`: single loop var (child0) is an int
|
||||
if int(key_id) >= 0 {
|
||||
key_name := t.a.nodes[int(key_id)].value
|
||||
if key_name.len > 0 {
|
||||
t.set_var_type(key_name, 'int')
|
||||
}
|
||||
}
|
||||
} else if has_index {
|
||||
// two loop vars: child0 = key/index, child1 = value/element
|
||||
key_name := if int(key_id) >= 0 { t.a.nodes[int(key_id)].value } else { '' }
|
||||
val_name := if int(val_id) >= 0 { t.a.nodes[int(val_id)].value } else { '' }
|
||||
if iter_type.starts_with('map[') {
|
||||
// map[K]V: child0 (key) -> key type, child1 (val) -> value type
|
||||
bracket_end := iter_type.index(']') or { 0 }
|
||||
if key_name.len > 0 && bracket_end > 4 {
|
||||
t.set_var_type(key_name, iter_type[4..bracket_end])
|
||||
}
|
||||
} else if iter_type.starts_with('[]') || iter_type == 'string' {
|
||||
// []E: child0 (index) -> 'int'
|
||||
if key_name.len > 0 {
|
||||
t.set_var_type(key_name, 'int')
|
||||
}
|
||||
}
|
||||
if val_name.len > 0 {
|
||||
elem_type := t.infer_for_in_elem_type(iter_type, node)
|
||||
if elem_type.len > 0 {
|
||||
t.set_var_type(val_name, elem_type)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// single var, child0 is the element
|
||||
if int(key_id) >= 0 {
|
||||
key_name := t.a.nodes[int(key_id)].value
|
||||
if key_name.len > 0 {
|
||||
elem_type := t.infer_for_in_elem_type(iter_type, node)
|
||||
if elem_type.len > 0 {
|
||||
t.set_var_type(key_name, elem_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mut ids := []flat.NodeId{}
|
||||
ids << key_id
|
||||
ids << val_id
|
||||
ids << new_container
|
||||
if header_count == 4 {
|
||||
range_end_id := t.a.child(&node, 3)
|
||||
ids << t.transform_expr(range_end_id)
|
||||
}
|
||||
body_ids := t.a.children_of(&node)[header_count..].clone()
|
||||
new_body := t.transform_stmts(body_ids)
|
||||
for bid in new_body {
|
||||
ids << bid
|
||||
}
|
||||
start := t.a.children.len
|
||||
for cid in ids {
|
||||
t.a.children << cid
|
||||
}
|
||||
return arr1(t.a.add_node(flat.Node{
|
||||
kind: .for_in_stmt
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(ids.len)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
}))
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_range_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, low_id flat.NodeId, high_id flat.NodeId, body_ids []flat.NodeId) []flat.NodeId {
|
||||
if int(key_id) < 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
key := t.a.nodes[int(key_id)]
|
||||
if key.kind != .ident || key.value.len == 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
t.set_var_type(key.value, 'int')
|
||||
low := t.transform_expr(low_id)
|
||||
high := t.stable_expr_for_reuse(high_id)
|
||||
mut prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut prefix)
|
||||
init := t.make_decl_assign_typed(key.value, low, 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(key.value), high)
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(key.value), .inc))
|
||||
new_body := t.transform_stmts(body_ids)
|
||||
prefix << t.make_for_stmt(init, cond, post, new_body, node)
|
||||
return prefix
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_indexed_for_in(id flat.NodeId, node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, iter_type string, has_index bool, body_ids []flat.NodeId) []flat.NodeId {
|
||||
if int(key_id) < 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
key := t.a.nodes[int(key_id)]
|
||||
if key.kind != .ident || key.value.len == 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
mut container := t.stable_expr_for_reuse(container_id)
|
||||
mut prefix := []flat.NodeId{}
|
||||
t.drain_pending(mut prefix)
|
||||
mut actual_iter_type := iter_type
|
||||
container_type := t.node_type(container)
|
||||
if container_type.len > 0 {
|
||||
actual_iter_type = container_type
|
||||
}
|
||||
if actual_iter_type.starts_with('&[]') {
|
||||
container = t.make_prefix(.mul, container)
|
||||
t.a.nodes[int(container)].typ = actual_iter_type[1..]
|
||||
actual_iter_type = actual_iter_type[1..]
|
||||
}
|
||||
elem_type := t.infer_for_in_elem_type(actual_iter_type, node)
|
||||
if elem_type.len == 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
mut idx_name := key.value
|
||||
if !has_index {
|
||||
idx_name = t.new_temp('for_idx')
|
||||
}
|
||||
mut elem_name := key.value
|
||||
if has_index {
|
||||
if int(val_id) < 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
val := t.a.nodes[int(val_id)]
|
||||
if val.kind != .ident || val.value.len == 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
elem_name = val.value
|
||||
}
|
||||
t.set_var_type(idx_name, 'int')
|
||||
elem_is_mut := node.op == .amp && actual_iter_type != 'string'
|
||||
elem_needs_ref := elem_is_mut && !elem_type.starts_with('&')
|
||||
elem_var_type := if elem_needs_ref { '&${elem_type}' } else { elem_type }
|
||||
t.set_var_type(elem_name, elem_var_type)
|
||||
len_expr := if is_fixed_array_type(actual_iter_type) {
|
||||
t.make_fixed_array_len_expr(actual_iter_type)
|
||||
} else {
|
||||
t.make_selector(container, 'len', 'int')
|
||||
}
|
||||
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
||||
cond := t.make_infix(.lt, t.make_ident(idx_name), len_expr)
|
||||
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
||||
elem_expr := if elem_needs_ref && actual_iter_type.starts_with('[]') {
|
||||
t.array_get_ptr(container, t.make_ident(idx_name), elem_type)
|
||||
} else if elem_needs_ref {
|
||||
t.make_prefix(.amp, t.make_index(container, t.make_ident(idx_name), elem_type))
|
||||
} else if actual_iter_type.starts_with('[]') {
|
||||
t.array_get_value(container, t.make_ident(idx_name), elem_type)
|
||||
} else {
|
||||
t.make_index(container, t.make_ident(idx_name), elem_type)
|
||||
}
|
||||
elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_var_type)
|
||||
mut transformed_body := []flat.NodeId{}
|
||||
if elem_needs_ref {
|
||||
had_pointer_value_lvalue := t.pointer_value_lvalues[elem_name] or { false }
|
||||
t.pointer_value_lvalues[elem_name] = true
|
||||
transformed_body = t.transform_stmts(body_ids)
|
||||
if had_pointer_value_lvalue {
|
||||
t.pointer_value_lvalues[elem_name] = true
|
||||
} else {
|
||||
t.pointer_value_lvalues.delete(elem_name)
|
||||
}
|
||||
} else {
|
||||
transformed_body = t.transform_stmts(body_ids)
|
||||
}
|
||||
mut new_body := []flat.NodeId{}
|
||||
new_body << elem_decl
|
||||
new_body << transformed_body
|
||||
prefix << t.make_for_stmt(init, cond, post, new_body, node)
|
||||
return prefix
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_for_stmt(init flat.NodeId, cond flat.NodeId, post flat.NodeId, body []flat.NodeId, src flat.Node) flat.NodeId {
|
||||
start := t.a.children.len
|
||||
t.a.children << init
|
||||
t.a.children << cond
|
||||
t.a.children << post
|
||||
for id in body {
|
||||
t.a.children << id
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .for_stmt
|
||||
op: src.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(3 + body.len)
|
||||
pos: src.pos
|
||||
typ: src.typ
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) detect_for_in_type(node flat.Node) string {
|
||||
if node.typ.len > 0 {
|
||||
return node.typ
|
||||
}
|
||||
header_count := node.value.int()
|
||||
container_idx := if header_count >= 3 { header_count - 1 } else { 2 }
|
||||
if node.children_count > container_idx {
|
||||
iter_id := t.a.child(&node, container_idx)
|
||||
return t.node_type(iter_id)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// Infer the element type for the loop variable from the iterable type.
|
||||
fn (t &Transformer) infer_for_in_elem_type(iter_type string, node flat.Node) string {
|
||||
if iter_type.starts_with('&[]') {
|
||||
return iter_type[3..]
|
||||
}
|
||||
if iter_type.starts_with('[]') {
|
||||
return iter_type[2..]
|
||||
}
|
||||
if iter_type.starts_with('map[') {
|
||||
// map[K]V -> value type is everything after the closing ']'
|
||||
bracket_end := iter_type.index(']') or { return '' }
|
||||
if bracket_end + 1 < iter_type.len {
|
||||
return iter_type[bracket_end + 1..]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
if iter_type == 'string' {
|
||||
return 'u8'
|
||||
}
|
||||
if is_fixed_array_type(iter_type) {
|
||||
return fixed_array_elem_type(iter_type)
|
||||
}
|
||||
// Check if the iterable is a range expression
|
||||
if node.children_count > 0 {
|
||||
iter_id := t.a.child(&node, 0)
|
||||
iter_node := t.a.nodes[int(iter_id)]
|
||||
if iter_node.kind == .range {
|
||||
return 'int'
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,213 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
// is_interface_type checks if a type name is a known interface.
|
||||
fn (t &Transformer) is_interface_type(name string) bool {
|
||||
return t.resolve_interface_type_name(name).len > 0
|
||||
}
|
||||
|
||||
fn (t &Transformer) resolve_interface_type_name(name string) string {
|
||||
if name.len == 0 || isnil(t.tc) {
|
||||
return ''
|
||||
}
|
||||
clean := t.trim_pointer_type(t.normalize_type_alias(name))
|
||||
if clean in t.tc.interface_names {
|
||||
return clean
|
||||
}
|
||||
if !clean.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${clean}'
|
||||
if qname in t.tc.interface_names {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_interface_value_for_type(id flat.NodeId, target_type string) ?flat.NodeId {
|
||||
if int(id) < 0 || target_type.len == 0 || isnil(t.tc) {
|
||||
return none
|
||||
}
|
||||
target_is_ptr := target_type.starts_with('&')
|
||||
iface_name := t.resolve_interface_type_name(target_type)
|
||||
if iface_name.len == 0 {
|
||||
return none
|
||||
}
|
||||
// IError has bespoke handling (built via `error()`, fields accessed directly);
|
||||
// do not route it through the generic interface boxing.
|
||||
if iface_name.all_after_last('.') == 'IError' {
|
||||
return none
|
||||
}
|
||||
source_type := t.node_type(id)
|
||||
source_iface := t.resolve_interface_type_name(source_type)
|
||||
if source_iface == iface_name {
|
||||
return t.transform_expr(id)
|
||||
}
|
||||
if target_is_ptr && source_type.starts_with('&')
|
||||
&& t.resolve_interface_type_name(source_type[1..]) == iface_name {
|
||||
return t.transform_expr(id)
|
||||
}
|
||||
literal := t.make_interface_literal_from_expr(id, iface_name) or { return none }
|
||||
if !target_is_ptr {
|
||||
return literal
|
||||
}
|
||||
// A `&Interface` value must outlive the current scope (it is commonly returned
|
||||
// or stored in a global, e.g. `default_rng = &PRNG(rng)`). Heap-allocate the
|
||||
// interface box rather than taking the address of a local temporary, which
|
||||
// would dangle.
|
||||
tmp_name := t.new_temp('iface_box')
|
||||
t.pending_stmts << t.make_decl_assign_typed(tmp_name, literal, iface_name)
|
||||
addr := t.make_prefix(.amp, t.make_ident(tmp_name))
|
||||
size := t.make_sizeof_type(iface_name)
|
||||
dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
|
||||
cast := t.make_cast(target_type, dup, target_type)
|
||||
t.a.nodes[int(cast)].typ = target_type
|
||||
return cast
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_global_amp_interface_cast(node flat.Node, target_type string) ?flat.NodeId {
|
||||
if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
|
||||
return none
|
||||
}
|
||||
child_id := t.a.child(&node, 0)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind != .cast_expr || child.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
iface_name := t.resolve_interface_type_name(child.value)
|
||||
if iface_name.len == 0 || iface_name.all_after_last('.') == 'IError' {
|
||||
return none
|
||||
}
|
||||
old_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
literal := t.make_interface_literal_from_expr(t.a.child(&child, 0), iface_name) or {
|
||||
t.pending_stmts = old_pending
|
||||
return none
|
||||
}
|
||||
has_pending := t.pending_stmts.len > 0
|
||||
t.pending_stmts.clear()
|
||||
t.pending_stmts = old_pending
|
||||
if has_pending {
|
||||
return none
|
||||
}
|
||||
start := t.a.children.len
|
||||
t.a.children << literal
|
||||
ptr_type := if target_type.len > 0 { target_type } else { '&${iface_name}' }
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .prefix
|
||||
op: .amp
|
||||
children_start: start
|
||||
children_count: 1
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: ptr_type
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_interface_literal_from_expr(id flat.NodeId, iface_name string) ?flat.NodeId {
|
||||
fields := t.tc.interface_fields[iface_name] or { []types.StructField{} }
|
||||
source_type := t.node_type(id)
|
||||
if source_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
source_expr := t.transform_expr(id)
|
||||
source := t.stable_transformed_expr_for_reuse(source_expr, source_type, 'iface_src')
|
||||
is_ptr := source_type.starts_with('&')
|
||||
concrete_type := if is_ptr { source_type[1..] } else { source_type }
|
||||
// `_object` is a pointer to the boxed concrete value; method dispatch reads it
|
||||
// back and casts it to the concrete type. For pointer sources we store the
|
||||
// pointer directly; for value sources we heap-copy so the box can outlive the
|
||||
// source scope. The pointer is typed (`&Concrete`) so codegen can recover the
|
||||
// concrete type and emit the matching `_typ` dispatch id.
|
||||
object_expr := if is_ptr {
|
||||
source
|
||||
} else {
|
||||
addr := t.make_prefix(.amp, source)
|
||||
size := t.make_sizeof_type(concrete_type)
|
||||
dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
|
||||
t.make_cast('&${concrete_type}', dup, '&${concrete_type}')
|
||||
}
|
||||
field_base := if is_ptr {
|
||||
base := t.make_prefix(.mul, source)
|
||||
t.a.nodes[int(base)].typ = concrete_type
|
||||
base
|
||||
} else {
|
||||
source
|
||||
}
|
||||
mut field_ids := []flat.NodeId{cap: fields.len + 1}
|
||||
field_ids << t.make_sum_literal_field('_object', object_expr, '&${concrete_type}')
|
||||
for field in fields {
|
||||
field_type := t.normalize_type_alias(field.typ.name())
|
||||
field_value := t.make_selector(field_base, field.name, field_type)
|
||||
field_ids << t.make_sum_literal_field(field.name, field_value, field_type)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for field_id in field_ids {
|
||||
t.a.children << field_id
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .struct_init
|
||||
children_start: start
|
||||
children_count: flat.child_count(field_ids.len)
|
||||
value: iface_name
|
||||
typ: iface_name
|
||||
})
|
||||
}
|
||||
|
||||
// transform_interface_cast transforms interface-to-concrete type casts.
|
||||
// This is a hook for future interface dispatch lowering where interface
|
||||
// values need to be unwrapped to their concrete types.
|
||||
// Currently passes through unchanged.
|
||||
fn (mut t Transformer) transform_interface_cast(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
new_children << t.transform_expr(child_id)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for nc in new_children {
|
||||
t.a.children << nc
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: node.kind
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: node.children_count
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
|
||||
// transform_interface_method_call transforms method calls on interface values.
|
||||
// This is a hook for vtable dispatch lowering where `iface.method(args)`
|
||||
// needs to be rewritten to indirect calls through the interface vtable.
|
||||
// Currently passes through unchanged.
|
||||
fn (mut t Transformer) transform_interface_method_call(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
new_children << t.transform_expr(child_id)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for nc in new_children {
|
||||
t.a.children << nc
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: node.kind
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: node.children_count
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
fn (mut t Transformer) transform_live_fn(id flat.NodeId, _node flat.Node) flat.NodeId {
|
||||
return id
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
struct MapIndexInfo {
|
||||
base_id flat.NodeId
|
||||
key_id flat.NodeId
|
||||
base_type string
|
||||
key_type string
|
||||
value_type string
|
||||
}
|
||||
|
||||
fn (mut t Transformer) map_type_parts(map_type string) (string, string) {
|
||||
clean := t.clean_map_type(map_type)
|
||||
if !clean.starts_with('map[') {
|
||||
return '', ''
|
||||
}
|
||||
return t.map_key_type(clean), t.map_value_type(clean)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_new_map_call(map_type string) flat.NodeId {
|
||||
key_type, value_type := t.map_type_parts(map_type)
|
||||
hash_fn, eq_fn, clone_fn, free_fn := map_callback_names(key_type)
|
||||
mut args := []flat.NodeId{}
|
||||
args << t.make_sizeof_type(key_type)
|
||||
args << t.make_sizeof_type(value_type)
|
||||
args << t.make_ident(hash_fn)
|
||||
args << t.make_ident(eq_fn)
|
||||
args << t.make_ident(clone_fn)
|
||||
args << t.make_ident(free_fn)
|
||||
return t.make_call_typed('new_map', args, map_type)
|
||||
}
|
||||
|
||||
fn map_callback_names(key_type string) (string, string, string, string) {
|
||||
if key_type == 'string' {
|
||||
return 'v3_map_hash_string', 'v3_map_eq_string', 'v3_map_clone_string', 'v3_map_free_string'
|
||||
}
|
||||
size_suffix := match key_type {
|
||||
'u8', 'i8', 'bool', 'char' { '1' }
|
||||
'u16', 'i16' { '2' }
|
||||
'i64', 'u64', 'isize', 'usize', 'voidptr' { '8' }
|
||||
else { '4' }
|
||||
}
|
||||
|
||||
return 'v3_map_hash_int_${size_suffix}', 'v3_map_eq_int_${size_suffix}', 'v3_map_clone_int_${size_suffix}', 'v3_map_free_nop'
|
||||
}
|
||||
|
||||
fn (mut t Transformer) map_index_info(index_id flat.NodeId) ?MapIndexInfo {
|
||||
if int(index_id) < 0 {
|
||||
return none
|
||||
}
|
||||
lhs := t.a.nodes[int(index_id)]
|
||||
if lhs.kind != .index || lhs.children_count < 2 || lhs.value == 'range' {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&lhs, 0)
|
||||
key_id := t.a.child(&lhs, 1)
|
||||
base_type := t.node_type(base_id)
|
||||
map_type := t.clean_map_type(base_type)
|
||||
if !map_type.starts_with('map[') {
|
||||
return none
|
||||
}
|
||||
key_type, value_type := t.map_type_parts(map_type)
|
||||
if key_type.len == 0 || value_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
return MapIndexInfo{
|
||||
base_id: base_id
|
||||
key_id: key_id
|
||||
base_type: base_type
|
||||
key_type: key_type
|
||||
value_type: value_type
|
||||
}
|
||||
}
|
||||
|
||||
fn (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 {
|
||||
call := t.make_call_typed('map__get', arr3(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
|
||||
t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(zero_name))), 'voidptr')
|
||||
cast := t.make_cast('&${value_type}', call, '&${value_type}')
|
||||
return t.make_prefix(.mul, cast)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_map_get_check_expr(map_expr flat.NodeId, base_type string, key_name string) flat.NodeId {
|
||||
return t.make_call_typed('map__get_check', arr2(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
|
||||
t.make_ident(key_name))), 'voidptr')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_map_exists_expr(map_expr flat.NodeId, base_type string, key_name string) flat.NodeId {
|
||||
return t.make_call_typed('map__exists', arr2(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
|
||||
t.make_ident(key_name))), 'bool')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_map_set_stmt(map_expr flat.NodeId, base_type string, key_name string, value_name string) flat.NodeId {
|
||||
call := t.make_call_typed('map__set', arr3(t.runtime_addr(map_expr, base_type), t.make_prefix(.amp,
|
||||
t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void')
|
||||
return t.make_expr_stmt(call)
|
||||
}
|
||||
|
||||
fn (t &Transformer) const_expr_for_ident(id flat.NodeId) ?flat.NodeId {
|
||||
if int(id) < 0 || isnil(t.tc) {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind != .ident || node.value.len == 0 {
|
||||
return none
|
||||
}
|
||||
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${node.value}'
|
||||
if expr_id := t.tc.const_exprs[qname] {
|
||||
return expr_id
|
||||
}
|
||||
}
|
||||
if expr_id := t.tc.const_exprs[node.value] {
|
||||
return expr_id
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_map_membership_expr(map_id flat.NodeId, key_id flat.NodeId, map_type string) ?flat.NodeId {
|
||||
clean_type := t.clean_map_type(map_type)
|
||||
mut key_type := ''
|
||||
if clean_type.starts_with('map[') {
|
||||
key_type = t.map_key_type(clean_type)
|
||||
}
|
||||
if key_type.len == 0 {
|
||||
key_type = t.node_type(key_id)
|
||||
}
|
||||
if key_type.len == 0 {
|
||||
key := t.a.nodes[int(key_id)]
|
||||
if key.kind == .selector {
|
||||
key_type = t.selector_field_type(key)
|
||||
}
|
||||
}
|
||||
if key_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
map_source_id := t.const_expr_for_ident(map_id) or { map_id }
|
||||
map_expr := t.stable_expr_for_reuse(map_source_id)
|
||||
key_name := t.new_temp('map_key')
|
||||
t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr(key_id), key_type)
|
||||
return t.make_map_exists_expr(map_expr, map_type, key_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) try_lower_map_index_expr(_id flat.NodeId, node flat.Node) ?flat.NodeId {
|
||||
if node.kind != .index || node.children_count < 2 || node.value == 'range' {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&node, 0)
|
||||
key_id := t.a.child(&node, 1)
|
||||
base_type := t.node_type(base_id)
|
||||
map_type := t.clean_map_type(base_type)
|
||||
if !map_type.starts_with('map[') {
|
||||
return none
|
||||
}
|
||||
key_type, value_type := t.map_type_parts(map_type)
|
||||
if key_type.len == 0 || value_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
map_source_id := t.const_expr_for_ident(base_id) or { base_id }
|
||||
map_expr := t.stable_expr_for_reuse(map_source_id)
|
||||
key_name := t.new_temp('map_key')
|
||||
zero_name := t.new_temp('map_zero')
|
||||
t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr(key_id), key_type)
|
||||
t.pending_stmts << t.make_decl_assign_typed(zero_name, t.zero_value_for_type(value_type),
|
||||
value_type)
|
||||
return t.make_map_get_expr(map_expr, base_type, key_name, zero_name, value_type)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) is_map_index_or_expr(node flat.Node) bool {
|
||||
if node.kind != .or_expr || node.children_count < 2 {
|
||||
return false
|
||||
}
|
||||
expr := t.a.child_node(&node, 0)
|
||||
if expr.kind != .index || expr.children_count < 2 || expr.value == 'range' {
|
||||
return false
|
||||
}
|
||||
base_id := t.a.child(expr, 0)
|
||||
base_type := t.node_type(base_id)
|
||||
return t.clean_map_type(base_type).starts_with('map[')
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_map_index_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count < 2 {
|
||||
return id
|
||||
}
|
||||
expr_id := t.a.child(&node, 0)
|
||||
body_id := t.a.child(&node, 1)
|
||||
info := t.map_index_info(expr_id) or { return id }
|
||||
map_expr := t.stable_expr_for_reuse(info.base_id)
|
||||
key_name := t.new_temp('map_key')
|
||||
ptr_name := t.new_temp('map_ptr')
|
||||
val_name := t.new_temp('map_val')
|
||||
outer_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
key_expr := t.transform_expr(info.key_id)
|
||||
mut prelude := []flat.NodeId{}
|
||||
t.drain_pending(mut prelude)
|
||||
prelude << t.make_decl_assign_typed(key_name, key_expr, info.key_type)
|
||||
prelude << t.make_decl_assign_typed(ptr_name, t.make_map_get_check_expr(map_expr,
|
||||
info.base_type, key_name), 'voidptr')
|
||||
prelude << t.make_decl_assign_typed(val_name, t.zero_value_for_type(info.value_type),
|
||||
info.value_type)
|
||||
|
||||
ptr_ident := t.make_ident(ptr_name)
|
||||
found_cond := t.make_infix(.ne, ptr_ident, t.a.add(.nil_literal))
|
||||
ptr_value := t.make_prefix(.mul, t.make_cast('&${info.value_type}', t.make_ident(ptr_name),
|
||||
'&${info.value_type}'))
|
||||
then_block := t.make_block(arr1(t.make_assign(t.make_ident(val_name), ptr_value)))
|
||||
else_block := t.make_block(t.lower_map_or_body_to_stmts(body_id, val_name, info.value_type,
|
||||
node.value))
|
||||
t.pending_stmts = outer_pending
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
t.pending_stmts << t.make_if(found_cond, then_block, else_block)
|
||||
return t.make_ident(val_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_map_or_body_to_stmts(body_id flat.NodeId, target_name string, target_type string, mode string) []flat.NodeId {
|
||||
if mode == '!' || mode == '?' {
|
||||
if t.is_optional_type_name(t.cur_fn_ret_type) {
|
||||
return arr1(t.make_none_return_stmt())
|
||||
}
|
||||
return arr1(t.make_panic_stmt('option/result propagation failed'))
|
||||
}
|
||||
if int(body_id) < 0 {
|
||||
return []flat.NodeId{}
|
||||
}
|
||||
body := t.a.nodes[int(body_id)]
|
||||
if body.kind != .block {
|
||||
if body.kind == .none_expr && !t.is_optional_type_name(target_type)
|
||||
&& t.is_optional_type_name(t.cur_fn_ret_type) {
|
||||
return arr1(t.make_none_return_stmt())
|
||||
}
|
||||
return arr1(t.make_assign(t.make_ident(target_name), t.transform_expr(body_id)))
|
||||
}
|
||||
mut result := []flat.NodeId{}
|
||||
for i in 0 .. body.children_count {
|
||||
child_id := t.a.child(&body, i)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
is_last := i == body.children_count - 1
|
||||
if is_last && child.kind == .expr_stmt && child.children_count > 0 {
|
||||
inner_id := t.a.child(&child, 0)
|
||||
inner := t.a.nodes[int(inner_id)]
|
||||
if inner.kind == .none_expr && !t.is_optional_type_name(target_type)
|
||||
&& t.is_optional_type_name(t.cur_fn_ret_type) {
|
||||
result << t.make_none_return_stmt()
|
||||
continue
|
||||
}
|
||||
if t.node_type(inner_id) == 'void' {
|
||||
expanded := t.transform_stmt(child_id)
|
||||
t.drain_pending(mut result)
|
||||
for eid in expanded {
|
||||
result << eid
|
||||
}
|
||||
} else {
|
||||
value := t.transform_expr(inner_id)
|
||||
t.drain_pending(mut result)
|
||||
result << t.make_assign(t.make_ident(target_name), value)
|
||||
}
|
||||
} else {
|
||||
expanded := t.transform_stmt(child_id)
|
||||
t.drain_pending(mut result)
|
||||
for eid in expanded {
|
||||
result << eid
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = target_type
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut t Transformer) try_lower_map_index_assign(node flat.Node) ?[]flat.NodeId {
|
||||
if node.kind !in [.assign, .index_assign] || node.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
info := t.map_index_info(t.a.child(&node, 0)) or { return none }
|
||||
map_expr := t.stable_expr_for_reuse(info.base_id)
|
||||
key_name := t.new_temp('map_key')
|
||||
mut result := []flat.NodeId{}
|
||||
t.drain_pending(mut result)
|
||||
result << t.make_decl_assign_typed(key_name, t.transform_expr(info.key_id), info.key_type)
|
||||
rhs_id := t.a.child(&node, 1)
|
||||
if node.op == .assign {
|
||||
value_name := t.new_temp('map_val')
|
||||
value := if info.value_type.starts_with('&') && t.is_sum_type_name(info.value_type[1..]) {
|
||||
t.transform_expr_for_type(rhs_id, info.value_type)
|
||||
} else if info.value_type in t.sum_types
|
||||
|| t.resolve_sum_name(info.value_type) in t.sum_types {
|
||||
t.wrap_sum_value(rhs_id, info.value_type)
|
||||
} else {
|
||||
t.transform_expr(rhs_id)
|
||||
}
|
||||
result << t.make_decl_assign_typed(value_name, value, info.value_type)
|
||||
result << t.make_map_set_stmt(map_expr, info.base_type, key_name, value_name)
|
||||
return result
|
||||
}
|
||||
if node.op == .left_shift_assign && info.value_type.starts_with('[]') {
|
||||
t.lower_map_index_append_with_info(info, map_expr, key_name, rhs_id, mut result)
|
||||
return result
|
||||
}
|
||||
op := map_compound_to_infix_op(node.op) or { return none }
|
||||
t.lower_map_index_compound_with_info(info, map_expr, key_name, op, rhs_id, mut result)
|
||||
return result
|
||||
}
|
||||
|
||||
fn map_compound_to_infix_op(op flat.Op) ?flat.Op {
|
||||
match op {
|
||||
.plus_assign { return flat.Op.plus }
|
||||
.minus_assign { return flat.Op.minus }
|
||||
.mul_assign { return flat.Op.mul }
|
||||
.div_assign { return flat.Op.div }
|
||||
.mod_assign { return flat.Op.mod }
|
||||
.amp_assign { return flat.Op.amp }
|
||||
.pipe_assign { return flat.Op.pipe }
|
||||
.xor_assign { return flat.Op.xor }
|
||||
.left_shift_assign { return flat.Op.left_shift }
|
||||
.right_shift_assign { return flat.Op.right_shift }
|
||||
else { return none }
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut t Transformer) load_map_index_current(info MapIndexInfo, map_expr flat.NodeId, key_name string, mut result []flat.NodeId) string {
|
||||
zero_name := t.new_temp('map_zero')
|
||||
current_name := t.new_temp('map_val')
|
||||
result << t.make_decl_assign_typed(zero_name, t.zero_value_for_type(info.value_type),
|
||||
info.value_type)
|
||||
get_expr := t.make_map_get_expr(map_expr, info.base_type, key_name, zero_name, info.value_type)
|
||||
result << t.make_decl_assign_typed(current_name, get_expr, info.value_type)
|
||||
return current_name
|
||||
}
|
||||
|
||||
fn (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) {
|
||||
current_name := t.load_map_index_current(info, map_expr, key_name, mut result)
|
||||
rhs := t.transform_expr(rhs_id)
|
||||
new_value := if info.value_type == 'string' && op == .plus {
|
||||
t.make_call_typed('string__plus', arr2(t.make_ident(current_name), rhs), 'string')
|
||||
} else {
|
||||
t.make_infix(op, t.make_ident(current_name), rhs)
|
||||
}
|
||||
result << t.make_assign(t.make_ident(current_name), new_value)
|
||||
result << t.make_map_set_stmt(map_expr, info.base_type, key_name, current_name)
|
||||
}
|
||||
|
||||
fn (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) {
|
||||
current_name := t.load_map_index_current(info, map_expr, key_name, mut result)
|
||||
infix_op := if op == .dec { flat.Op.minus } else { flat.Op.plus }
|
||||
new_value := t.make_infix(infix_op, t.make_ident(current_name), t.make_int_literal(1))
|
||||
result << t.make_assign(t.make_ident(current_name), new_value)
|
||||
result << t.make_map_set_stmt(map_expr, info.base_type, key_name, current_name)
|
||||
}
|
||||
|
||||
fn (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) {
|
||||
current_name := t.load_map_index_current(info, map_expr, key_name, mut result)
|
||||
append := t.make_infix(.left_shift, t.make_ident(current_name), t.transform_expr(rhs_id))
|
||||
t.annotate_left_shift(append)
|
||||
result << t.make_expr_stmt(append)
|
||||
result << t.make_map_set_stmt(map_expr, info.base_type, key_name, current_name)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) try_lower_map_index_postfix_stmt(id flat.NodeId) ?[]flat.NodeId {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind != .postfix || node.children_count == 0 || node.op !in [.inc, .dec] {
|
||||
return none
|
||||
}
|
||||
info := t.map_index_info(t.a.child(&node, 0)) or { return none }
|
||||
map_expr := t.stable_expr_for_reuse(info.base_id)
|
||||
key_name := t.new_temp('map_key')
|
||||
mut result := []flat.NodeId{}
|
||||
t.drain_pending(mut result)
|
||||
result << t.make_decl_assign_typed(key_name, t.transform_expr(info.key_id), info.key_type)
|
||||
t.lower_map_index_postfix_with_info(info, map_expr, key_name, node.op, mut result)
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut t Transformer) try_lower_map_index_append_stmt(id flat.NodeId) ?[]flat.NodeId {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind != .infix || node.op != .left_shift || node.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
info := t.map_index_info(t.a.child(&node, 0)) or { return none }
|
||||
if !info.value_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
map_expr := t.stable_expr_for_reuse(info.base_id)
|
||||
key_name := t.new_temp('map_key')
|
||||
mut result := []flat.NodeId{}
|
||||
t.drain_pending(mut result)
|
||||
result << t.make_decl_assign_typed(key_name, t.transform_expr(info.key_id), info.key_type)
|
||||
t.lower_map_index_append_with_info(info, map_expr, key_name, t.a.child(&node, 1), mut result)
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_map_init_to_runtime(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
map_type := if node.value.len > 0 {
|
||||
node.value
|
||||
} else if node.typ.len > 0 {
|
||||
node.typ
|
||||
} else {
|
||||
t.node_type(id)
|
||||
}
|
||||
if !map_type.starts_with('map[') {
|
||||
return id
|
||||
}
|
||||
init_call := t.make_new_map_call(map_type)
|
||||
if node.children_count == 0 {
|
||||
return init_call
|
||||
}
|
||||
tmp_name := t.new_temp('map_lit')
|
||||
t.pending_stmts << t.make_decl_assign_typed(tmp_name, init_call, map_type)
|
||||
key_type, value_type := t.map_type_parts(map_type)
|
||||
for i := 0; i + 1 < node.children_count; i += 2 {
|
||||
key_name := t.new_temp('map_key')
|
||||
value_name := t.new_temp('map_val')
|
||||
t.pending_stmts << t.make_decl_assign_typed(key_name,
|
||||
t.transform_expr(t.a.child(&node, i)), key_type)
|
||||
value_id := t.a.child(&node, i + 1)
|
||||
value := if value_type.starts_with('&') && t.is_sum_type_name(value_type[1..]) {
|
||||
t.transform_expr_for_type(value_id, value_type)
|
||||
} else if value_type in t.sum_types || t.resolve_sum_name(value_type) in t.sum_types {
|
||||
t.wrap_sum_value(value_id, value_type)
|
||||
} else {
|
||||
t.transform_expr(value_id)
|
||||
}
|
||||
t.pending_stmts << t.make_decl_assign_typed(value_name, value, value_type)
|
||||
call := t.make_call_typed('map__set', arr3(t.make_prefix(.amp, t.make_ident(tmp_name)), t.make_prefix(.amp,
|
||||
t.make_ident(key_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void')
|
||||
t.pending_stmts << t.make_expr_stmt(call)
|
||||
}
|
||||
return t.make_ident(tmp_name)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
// match_has_sum_type_subject checks if a match_stmt's subject expression
|
||||
// resolves to a sum type. This determines whether the match needs to be
|
||||
// lowered with is_expr conditions (sum type dispatch) or plain equality
|
||||
// checks (value matching).
|
||||
fn (mut t Transformer) match_has_sum_type_subject(node flat.Node) bool {
|
||||
if node.children_count == 0 {
|
||||
return false
|
||||
}
|
||||
subject_id := t.a.child(&node, 0)
|
||||
subject_type := t.resolve_expr_type(subject_id)
|
||||
if subject_type.len == 0 {
|
||||
return false
|
||||
}
|
||||
return subject_type in t.sum_types
|
||||
}
|
||||
|
||||
// resolve_match_subject_type determines the type of a match subject expression.
|
||||
// Uses resolve_expr_type on child 0 (the match expression).
|
||||
fn (mut t Transformer) resolve_match_subject_type(node flat.Node) string {
|
||||
if node.children_count == 0 {
|
||||
return ''
|
||||
}
|
||||
subject_id := t.a.child(&node, 0)
|
||||
return t.resolve_expr_type(subject_id)
|
||||
}
|
||||
|
||||
// transform_match_branch_body transforms a match branch body with smartcast
|
||||
// awareness. When the branch matches a sum type variant, the subject variable
|
||||
// should be narrowed to that variant's type within the branch body.
|
||||
//
|
||||
// For now, this performs basic body transformation without smartcast injection.
|
||||
// When smartcast support is fully wired up, this will:
|
||||
// 1. Push a smartcast context for the subject variable narrowed to the variant
|
||||
// 2. Transform the body statements under that context
|
||||
// 3. Pop the smartcast context
|
||||
// 4. Rebuild the block with the transformed statements
|
||||
fn (mut t Transformer) transform_match_branch_body(branch_id flat.NodeId, subject_name string, variant_name string) flat.NodeId {
|
||||
if int(branch_id) < 0 {
|
||||
return branch_id
|
||||
}
|
||||
branch := t.a.nodes[int(branch_id)]
|
||||
if branch.children_count == 0 {
|
||||
return branch_id
|
||||
}
|
||||
// Collect the body statement ids from the branch
|
||||
mut body_ids := []flat.NodeId{}
|
||||
// Determine where the body starts (after the condition values)
|
||||
n_conds := t.count_conds(branch)
|
||||
for i in n_conds .. branch.children_count {
|
||||
body_ids << t.a.child(&branch, i)
|
||||
}
|
||||
if body_ids.len == 0 {
|
||||
return branch_id
|
||||
}
|
||||
// Push smartcast if we have a sum type variant match
|
||||
mut has_sc := subject_name.len > 0 && variant_name.len > 0
|
||||
if has_sc {
|
||||
sum_type_name := t.find_sum_type_for_variant(variant_name)
|
||||
if sum_type_name.len > 0 {
|
||||
t.push_smartcast(subject_name, variant_name, sum_type_name)
|
||||
} else {
|
||||
has_sc = false
|
||||
}
|
||||
}
|
||||
// Transform the body statements under smartcast context
|
||||
new_body := t.transform_stmts(body_ids)
|
||||
if has_sc {
|
||||
t.pop_smartcast()
|
||||
}
|
||||
return t.make_block(new_body)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
module transform
|
||||
|
||||
// is_generic_fn checks if a function name refers to a generic function.
|
||||
// Generic functions have type parameters that need to be monomorphized
|
||||
// into concrete specializations before code generation.
|
||||
// For now, returns false as generics are not yet supported in v3.
|
||||
fn (mut t Transformer) is_generic_fn(_name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// is_generic_struct checks if a struct name is generic.
|
||||
// Generic structs like `Array[T]` would need monomorphization into
|
||||
// concrete types like `Array_int`, `Array_string`, etc.
|
||||
// For now, returns false as generics are not yet supported in v3.
|
||||
fn (mut t Transformer) is_generic_struct(_name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// monomorphize_pass is the top-level generic monomorphization pass.
|
||||
// When generics support is added, this will:
|
||||
// 1. Scan all call sites and struct inits for generic instantiations
|
||||
// 2. For each unique set of type arguments, clone and specialize the
|
||||
// generic function/struct definition
|
||||
// 3. Rewrite call sites to reference the monomorphized specialization
|
||||
// 4. Remove the original generic definitions
|
||||
// Called from transform_all() in transform.v if generics support is added.
|
||||
fn (mut t Transformer) monomorphize_pass() {
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
struct ArrayIndexInfo {
|
||||
base_id flat.NodeId
|
||||
index_id flat.NodeId
|
||||
base_type string
|
||||
value_type string
|
||||
}
|
||||
|
||||
struct EnumFromStringInfo {
|
||||
enum_type string
|
||||
fields []string
|
||||
arg_id flat.NodeId
|
||||
}
|
||||
|
||||
fn (t &Transformer) optional_base_type(typ string) string {
|
||||
if typ.len > 1 && (typ[0] == `?` || typ[0] == `!`) {
|
||||
return typ[1..]
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (mut t Transformer) array_index_info(index_id flat.NodeId) ?ArrayIndexInfo {
|
||||
if int(index_id) < 0 {
|
||||
return none
|
||||
}
|
||||
expr := t.a.nodes[int(index_id)]
|
||||
if expr.kind != .index || expr.children_count < 2 || expr.value == 'range' {
|
||||
return none
|
||||
}
|
||||
base_id := t.a.child(&expr, 0)
|
||||
index_expr_id := t.a.child(&expr, 1)
|
||||
base_type := t.resolve_expr_type(base_id)
|
||||
if !base_type.starts_with('[]') {
|
||||
return none
|
||||
}
|
||||
value_type := t.normalize_type_alias(base_type[2..])
|
||||
if value_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
return ArrayIndexInfo{
|
||||
base_id: base_id
|
||||
index_id: index_expr_id
|
||||
base_type: base_type
|
||||
value_type: value_type
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut t Transformer) is_array_index_or_expr(node flat.Node) bool {
|
||||
if node.kind != .or_expr || node.children_count < 2 {
|
||||
return false
|
||||
}
|
||||
_ := t.array_index_info(t.a.child(&node, 0)) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_array_index_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count < 2 {
|
||||
return id
|
||||
}
|
||||
expr_id := t.a.child(&node, 0)
|
||||
body_id := t.a.child(&node, 1)
|
||||
info := t.array_index_info(expr_id) or { return id }
|
||||
base_expr := t.stable_expr_for_reuse(info.base_id)
|
||||
index_name := t.new_temp('arr_idx')
|
||||
val_name := t.new_temp('arr_val')
|
||||
outer_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
index_expr := t.transform_expr(info.index_id)
|
||||
mut prelude := []flat.NodeId{}
|
||||
t.drain_pending(mut prelude)
|
||||
prelude << t.make_decl_assign_typed(index_name, index_expr, 'int')
|
||||
prelude << t.make_decl_assign_typed(val_name, t.zero_value_for_type(info.value_type),
|
||||
info.value_type)
|
||||
|
||||
idx_ident := t.make_ident(index_name)
|
||||
lower_ok := t.make_infix(.ge, idx_ident, t.make_int_literal(0))
|
||||
upper_ok := t.make_infix(.lt, t.make_ident(index_name),
|
||||
t.make_selector(base_expr, 'len', 'int'))
|
||||
found_cond := t.make_infix(.logical_and, lower_ok, upper_ok)
|
||||
index_value := t.make_index(base_expr, t.make_ident(index_name), info.value_type)
|
||||
then_block := t.make_block(arr1(t.make_assign(t.make_ident(val_name), index_value)))
|
||||
else_block := t.make_block(t.lower_or_body_to_stmts(body_id, val_name, info.value_type,
|
||||
node.value, ''))
|
||||
t.pending_stmts = outer_pending
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
t.pending_stmts << t.make_if(found_cond, then_block, else_block)
|
||||
return t.make_ident(val_name)
|
||||
}
|
||||
|
||||
fn (t &Transformer) or_body_is_nil(body_id flat.NodeId) bool {
|
||||
if int(body_id) < 0 {
|
||||
return false
|
||||
}
|
||||
body := t.a.nodes[int(body_id)]
|
||||
if body.kind == .nil_literal {
|
||||
return true
|
||||
}
|
||||
if body.kind != .block || body.children_count != 1 {
|
||||
return false
|
||||
}
|
||||
stmt_id := t.a.child(&body, 0)
|
||||
stmt := t.a.nodes[int(stmt_id)]
|
||||
if stmt.kind == .expr_stmt && stmt.children_count == 1 {
|
||||
return t.a.nodes[int(t.a.child(&stmt, 0))].kind == .nil_literal
|
||||
}
|
||||
return stmt.kind == .nil_literal
|
||||
}
|
||||
|
||||
fn (t &Transformer) enum_from_string_info(expr_id flat.NodeId) ?EnumFromStringInfo {
|
||||
if int(expr_id) < 0 {
|
||||
return none
|
||||
}
|
||||
expr := t.a.nodes[int(expr_id)]
|
||||
if expr.kind != .call || expr.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
fn_id := t.a.child(&expr, 0)
|
||||
fn_node := t.a.nodes[int(fn_id)]
|
||||
if fn_node.kind != .selector || fn_node.value != 'from_string' || fn_node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
enum_id := t.a.child(&fn_node, 0)
|
||||
enum_type := t.enum_type_from_node(enum_id) or { return none }
|
||||
fields := t.enum_types[enum_type] or { return none }
|
||||
if fields.len == 0 {
|
||||
return none
|
||||
}
|
||||
return EnumFromStringInfo{
|
||||
enum_type: enum_type
|
||||
fields: fields.clone()
|
||||
arg_id: t.a.child(&expr, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn (t &Transformer) enum_type_from_node(id flat.NodeId) ?string {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .ident {
|
||||
if node.value in t.enum_types {
|
||||
return node.value
|
||||
}
|
||||
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${node.value}'
|
||||
if qname in t.enum_types {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
for key, _ in t.enum_types {
|
||||
if key.all_after_last('.') == node.value {
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.kind == .selector && node.children_count > 0 {
|
||||
base := t.a.child_node(&node, 0)
|
||||
if base.kind == .ident {
|
||||
qname := '${base.value}.${node.value}'
|
||||
if qname in t.enum_types {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut t Transformer) is_enum_from_string_or_expr(node flat.Node) bool {
|
||||
if node.kind != .or_expr || node.children_count < 2 {
|
||||
return false
|
||||
}
|
||||
_ := t.enum_from_string_info(t.a.child(&node, 0)) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_enum_from_string_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count < 2 {
|
||||
return id
|
||||
}
|
||||
expr_id := t.a.child(&node, 0)
|
||||
body_id := t.a.child(&node, 1)
|
||||
info := t.enum_from_string_info(expr_id) or { return id }
|
||||
str_name := t.new_temp('enum_str')
|
||||
val_name := t.new_temp('enum_val')
|
||||
outer_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
arg_expr := t.transform_expr(info.arg_id)
|
||||
mut prelude := []flat.NodeId{}
|
||||
t.drain_pending(mut prelude)
|
||||
prelude << t.make_decl_assign_typed(str_name, arg_expr, 'string')
|
||||
prelude << t.make_decl_assign_typed(val_name, t.zero_value_for_type(info.enum_type),
|
||||
info.enum_type)
|
||||
|
||||
mut else_block := t.make_block(t.lower_or_body_to_stmts(body_id, val_name, info.enum_type,
|
||||
node.value, ''))
|
||||
for i := info.fields.len - 1; i >= 0; i-- {
|
||||
cond := t.make_call_typed('string__eq', arr2(t.make_ident(str_name),
|
||||
t.make_string_literal(info.fields[i])), 'bool')
|
||||
assign := t.make_assign(t.make_ident(val_name), t.make_int_literal(i))
|
||||
then_block := t.make_block(arr1(assign))
|
||||
else_block = t.make_if(cond, then_block, else_block)
|
||||
}
|
||||
t.pending_stmts = outer_pending
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
t.pending_stmts << else_block
|
||||
return t.make_ident(val_name)
|
||||
}
|
||||
|
||||
fn (t &Transformer) or_expr_types(expr_id flat.NodeId, fallback_type string) (string, string) {
|
||||
if !isnil(t.tc) {
|
||||
if typ := t.tc.expr_type(expr_id) {
|
||||
if typ is types.OptionType {
|
||||
base_name := t.value_type_name(typ.base_type)
|
||||
source_name := if base_name == 'int' && typ.base_type is types.Void {
|
||||
'?void'
|
||||
} else {
|
||||
'?${base_name}'
|
||||
}
|
||||
return source_name, base_name
|
||||
}
|
||||
if typ is types.ResultType {
|
||||
base_name := t.value_type_name(typ.base_type)
|
||||
source_name := if base_name == 'int' && typ.base_type is types.Void {
|
||||
'!void'
|
||||
} else {
|
||||
'!${base_name}'
|
||||
}
|
||||
return source_name, base_name
|
||||
}
|
||||
}
|
||||
}
|
||||
mut expr_type := t.node_type(expr_id)
|
||||
if expr_type.len == 0 || expr_type == 'Optional' {
|
||||
if fallback_type.len > 0 && !t.is_optional_type_name(fallback_type) {
|
||||
expr_type = '?${fallback_type}'
|
||||
} else {
|
||||
expr_type = fallback_type
|
||||
}
|
||||
}
|
||||
base_type := t.optional_base_type(expr_type)
|
||||
mut value_type := if base_type.len > 0 { base_type } else { fallback_type }
|
||||
if value_type.len == 0 || value_type == 'void' || value_type == '!' || value_type == '?' {
|
||||
value_type = 'int'
|
||||
}
|
||||
return expr_type, value_type
|
||||
}
|
||||
|
||||
fn (t &Transformer) value_type_name(typ types.Type) string {
|
||||
if typ is types.Void {
|
||||
return 'void'
|
||||
}
|
||||
return typ.name()
|
||||
}
|
||||
|
||||
fn (t &Transformer) is_optional_type_name(typ string) bool {
|
||||
return typ.len > 0 && (typ[0] == `?` || typ[0] == `!`)
|
||||
}
|
||||
|
||||
fn (t &Transformer) qualify_optional_type(typ string) string {
|
||||
if typ.len < 2 || (typ[0] != `?` && typ[0] != `!`) {
|
||||
return typ
|
||||
}
|
||||
base := typ[1..]
|
||||
if base.contains('.') || base.len == 0 {
|
||||
return typ
|
||||
}
|
||||
qualified := t.qualify_type(base)
|
||||
if qualified != base {
|
||||
return typ[..1] + qualified
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (t &Transformer) qualify_type(name string) string {
|
||||
if name.contains('.') || name.len == 0 {
|
||||
return name
|
||||
}
|
||||
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${name}'
|
||||
if qname in t.sum_types || qname in t.structs {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
if qualified := t.qualified_types[name] {
|
||||
return qualified
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_decl_assign_typed(name string, rhs flat.NodeId, typ string) flat.NodeId {
|
||||
decl := t.make_decl_assign(name, rhs)
|
||||
if typ.len > 0 {
|
||||
t.a.nodes[int(decl)].typ = typ
|
||||
t.set_var_type(name, typ)
|
||||
}
|
||||
return decl
|
||||
}
|
||||
|
||||
fn (mut t Transformer) zero_value_for_type(typ string) flat.NodeId {
|
||||
mut clean := typ
|
||||
if clean.starts_with('&') {
|
||||
return t.a.add(.nil_literal)
|
||||
}
|
||||
if clean.len > 1 && (clean[0] == `?` || clean[0] == `!`) {
|
||||
return t.make_optional_none(clean)
|
||||
}
|
||||
clean = t.normalize_type_alias(clean)
|
||||
if clean.starts_with('fn(') || clean.starts_with('fn (') || clean.starts_with('fn_ptr:') {
|
||||
return t.make_cast(clean, t.make_int_literal(0), clean)
|
||||
}
|
||||
if clean.starts_with('[]') {
|
||||
return t.make_array_init(clean[2..])
|
||||
}
|
||||
if clean.starts_with('map[') {
|
||||
return t.make_map_init(clean)
|
||||
}
|
||||
if clean == 'string' {
|
||||
return t.make_string_literal('')
|
||||
}
|
||||
if clean == 'bool' {
|
||||
return t.make_bool_literal(false)
|
||||
}
|
||||
if clean in ['f32', 'f64'] {
|
||||
return t.make_float_literal('0.0')
|
||||
}
|
||||
if clean in ['void', ''] {
|
||||
return t.make_int_literal(0)
|
||||
}
|
||||
if clean in ['int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize', 'u8', 'byte', 'u16', 'u32', 'u64', 'rune', 'char']
|
||||
|| clean in t.enum_types {
|
||||
return t.make_int_literal(0)
|
||||
}
|
||||
return t.make_struct_init(clean)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_panic_stmt(message string) flat.NodeId {
|
||||
call := t.make_call('panic', arr1(t.make_string_literal(message)))
|
||||
return t.make_expr_stmt(call)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_none_return_stmt() flat.NodeId {
|
||||
return t.make_return(t.a.add(.none_expr), t.cur_fn_ret_type)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_none_return_stmt_with_err(err_source string) flat.NodeId {
|
||||
if err_source.len == 0 {
|
||||
return t.make_none_return_stmt()
|
||||
}
|
||||
err_expr := t.make_selector(t.make_ident(err_source), 'err', 'IError')
|
||||
return t.make_return(t.make_optional_none_with_err(t.cur_fn_ret_type, err_expr),
|
||||
t.cur_fn_ret_type)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_or_expr_to_temp(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count < 2 {
|
||||
return id
|
||||
}
|
||||
expr_id := t.a.child(&node, 0)
|
||||
body_id := t.a.child(&node, 1)
|
||||
expr_type, value_type := t.or_expr_types(expr_id, node.typ)
|
||||
if !t.is_optional_type_name(expr_type) {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
is_void := value_type.len == 0 || value_type == 'void'
|
||||
|
||||
opt_tmp := t.new_temp('or_opt')
|
||||
val_tmp := t.new_temp('or_val')
|
||||
|
||||
outer_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
new_expr := t.transform_expr(expr_id)
|
||||
mut prelude := []flat.NodeId{}
|
||||
t.drain_pending(mut prelude)
|
||||
|
||||
if is_void {
|
||||
prelude << t.make_decl_assign_typed(opt_tmp, new_expr, expr_type)
|
||||
opt_ident := t.make_ident(opt_tmp)
|
||||
not_ok := t.make_prefix(.not, t.make_selector(opt_ident, 'ok', 'bool'))
|
||||
else_block := t.make_block(t.lower_or_body_to_stmts(body_id, '', '', node.value, opt_tmp))
|
||||
if_stmt := t.make_if(not_ok, else_block, t.make_empty())
|
||||
t.pending_stmts = outer_pending
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
t.pending_stmts << if_stmt
|
||||
return t.make_int_literal(0)
|
||||
}
|
||||
|
||||
prelude << t.make_decl_assign_typed(opt_tmp, new_expr, expr_type)
|
||||
prelude << t.make_decl_assign_typed(val_tmp, t.zero_value_for_type(value_type), value_type)
|
||||
|
||||
opt_ident := t.make_ident(opt_tmp)
|
||||
ok_cond := t.make_selector(opt_ident, 'ok', 'bool')
|
||||
value_expr := t.make_selector(t.make_ident(opt_tmp), 'value', value_type)
|
||||
then_assign := t.make_assign(t.make_ident(val_tmp), value_expr)
|
||||
then_block := t.make_block(arr1(then_assign))
|
||||
else_block := t.make_block(t.lower_or_body_to_stmts(body_id, val_tmp, value_type, node.value,
|
||||
opt_tmp))
|
||||
if_stmt := t.make_if(ok_cond, then_block, else_block)
|
||||
t.pending_stmts = outer_pending
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
t.pending_stmts << if_stmt
|
||||
return t.make_ident(val_tmp)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) lower_or_body_to_stmts(body_id flat.NodeId, target_name string, target_type string, mode string, err_source string) []flat.NodeId {
|
||||
if mode == '!' || mode == '?' {
|
||||
if t.is_optional_type_name(t.cur_fn_ret_type) {
|
||||
return arr1(t.make_none_return_stmt_with_err(err_source))
|
||||
}
|
||||
return arr1(t.make_panic_stmt('option/result propagation failed'))
|
||||
}
|
||||
if int(body_id) < 0 {
|
||||
return []flat.NodeId{}
|
||||
}
|
||||
body := t.a.nodes[int(body_id)]
|
||||
if body.kind != .block {
|
||||
if target_name.len == 0 {
|
||||
value := t.transform_expr(body_id)
|
||||
mut result := []flat.NodeId{}
|
||||
t.drain_pending(mut result)
|
||||
if t.node_type(body_id) != 'void' {
|
||||
result << t.make_expr_stmt(value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
return arr1(t.make_assign(t.make_ident(target_name), t.transform_expr(body_id)))
|
||||
}
|
||||
mut result := []flat.NodeId{}
|
||||
if body.children_count == 0 {
|
||||
return result
|
||||
}
|
||||
t.set_var_type('err', 'IError')
|
||||
err_value := if err_source.len > 0 {
|
||||
t.make_selector(t.make_ident(err_source), 'err', 'IError')
|
||||
} else {
|
||||
t.make_struct_init('IError')
|
||||
}
|
||||
result << t.make_decl_assign_typed('err', err_value, 'IError')
|
||||
for i in 0 .. body.children_count {
|
||||
child_id := t.a.child(&body, i)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
is_last := i == body.children_count - 1
|
||||
if is_last && child.kind == .expr_stmt && child.children_count > 0 {
|
||||
inner_id := t.a.child(&child, 0)
|
||||
inner := t.a.nodes[int(inner_id)]
|
||||
if inner.kind == .call && t.is_noreturn_call(inner) {
|
||||
expanded := t.transform_stmt(child_id)
|
||||
t.drain_pending(mut result)
|
||||
for eid in expanded {
|
||||
result << eid
|
||||
}
|
||||
} else if t.node_type(inner_id) == 'void' {
|
||||
expanded := t.transform_stmt(child_id)
|
||||
t.drain_pending(mut result)
|
||||
for eid in expanded {
|
||||
result << eid
|
||||
}
|
||||
} else if target_name.len == 0 {
|
||||
expanded := t.transform_stmt(child_id)
|
||||
t.drain_pending(mut result)
|
||||
for eid in expanded {
|
||||
result << eid
|
||||
}
|
||||
} else {
|
||||
value := if target_type in t.sum_types
|
||||
|| t.resolve_sum_name(target_type) in t.sum_types {
|
||||
t.wrap_sum_value(inner_id, target_type)
|
||||
} else {
|
||||
t.transform_expr_for_type(inner_id, target_type)
|
||||
}
|
||||
t.drain_pending(mut result)
|
||||
result << t.make_assign(t.make_ident(target_name), value)
|
||||
}
|
||||
} else {
|
||||
expanded := t.transform_stmt(child_id)
|
||||
t.drain_pending(mut result)
|
||||
for eid in expanded {
|
||||
result << eid
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = target_type
|
||||
return result
|
||||
}
|
||||
|
||||
fn (t &Transformer) is_noreturn_call(node flat.Node) bool {
|
||||
if node.kind != .call || node.children_count == 0 {
|
||||
return false
|
||||
}
|
||||
fn_node := t.a.child_node(&node, 0)
|
||||
return fn_node.value in ['panic', 'exit']
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
// transform_sql_expr transforms SQL/ORM expressions into function calls.
|
||||
// V's ORM allows writing SQL-like queries directly in code:
|
||||
// users := sql db { select from User where age > 18 order by name }
|
||||
// This would be lowered into the appropriate database driver function calls.
|
||||
// For now, returns the node unchanged as a hook for future ORM support.
|
||||
fn (mut t Transformer) transform_sql_expr(id flat.NodeId, _node flat.Node) flat.NodeId {
|
||||
// TODO: When ORM support is implemented:
|
||||
// 1. Parse the SQL expression structure from children
|
||||
// 2. Determine the target table and struct type
|
||||
// 3. Build the appropriate db.exec() or db.select() call
|
||||
// 4. Generate result mapping from rows to struct instances
|
||||
// 5. Handle where clauses, order by, limit, etc.
|
||||
return id
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
// transform_return_with_sumtype_wrap checks if a return statement returns a
|
||||
// variant value where the function's return type is a sum type. In that case,
|
||||
// the variant value may need to be wrapped in the sum type's tagged union.
|
||||
//
|
||||
// For now, passes through unchanged since the C gen handles wrapping currently.
|
||||
// This is a hook for future sum type return wrapping at the transform level.
|
||||
fn (mut t Transformer) transform_return_with_sumtype_wrap(id flat.NodeId, node flat.Node) []flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return arr1(id)
|
||||
}
|
||||
// Check if current function returns a sum type
|
||||
if t.cur_fn_ret_type.len == 0 || t.cur_fn_ret_type !in t.sum_types {
|
||||
return arr1(id)
|
||||
}
|
||||
// Check if the return value is a struct init whose type is a variant
|
||||
child_id := t.a.child(&node, 0)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind == .struct_init && child.value.len > 0 {
|
||||
variants := t.sum_types[t.cur_fn_ret_type]
|
||||
for v in variants {
|
||||
if v == child.value {
|
||||
// This is a variant being returned as a sum type.
|
||||
// For now, pass through - C gen handles the wrapping.
|
||||
// TODO: Generate explicit sum type wrapping here.
|
||||
return arr1(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr1(id)
|
||||
}
|
||||
|
||||
// branch_tail_expr extracts the tail EXPRESSION id from a branch block,
|
||||
// unwrapping a trailing `.expr_stmt` so we get the expression inside it
|
||||
// rather than the statement wrapper.
|
||||
fn (t &Transformer) branch_tail_expr(block_id flat.NodeId) flat.NodeId {
|
||||
if int(block_id) < 0 {
|
||||
return block_id
|
||||
}
|
||||
block := t.a.nodes[int(block_id)]
|
||||
if block.kind != .block || block.children_count == 0 {
|
||||
return block_id
|
||||
}
|
||||
last_id := t.a.child(&block, block.children_count - 1)
|
||||
last := t.a.nodes[int(last_id)]
|
||||
if last.kind == .expr_stmt && last.children_count > 0 {
|
||||
return t.a.child(&last, 0)
|
||||
}
|
||||
return last_id
|
||||
}
|
||||
|
||||
// make_return builds a `return <val>` statement node with the given type.
|
||||
fn (mut t Transformer) make_return(val flat.NodeId, ret_typ string) flat.NodeId {
|
||||
start := t.a.children.len
|
||||
t.a.children << val
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .return_stmt
|
||||
children_start: start
|
||||
children_count: 1
|
||||
typ: ret_typ
|
||||
})
|
||||
}
|
||||
|
||||
fn (t &Transformer) return_expr_is_err(id flat.NodeId) bool {
|
||||
if int(id) < 0 {
|
||||
return false
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
return node.kind == .ident && node.value == 'err'
|
||||
}
|
||||
|
||||
fn (mut t Transformer) try_expand_return_optional_expr(node flat.Node) ?[]flat.NodeId {
|
||||
if node.children_count != 1 || !t.is_optional_type_name(t.cur_fn_ret_type) {
|
||||
return none
|
||||
}
|
||||
child_id := t.a.child(&node, 0)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind == .none_expr {
|
||||
return none
|
||||
}
|
||||
if !t.return_expr_is_optional_result(child_id) {
|
||||
return none
|
||||
}
|
||||
expr_type0 := t.optional_result_expr_type_name(child_id)
|
||||
if !t.is_optional_type_name(expr_type0) {
|
||||
return none
|
||||
}
|
||||
ret_type := t.qualify_optional_type(t.cur_fn_ret_type)
|
||||
expr_type := t.qualify_optional_type(expr_type0)
|
||||
new_expr := t.transform_expr(child_id)
|
||||
mut result := []flat.NodeId{}
|
||||
t.drain_pending(mut result)
|
||||
tmp_name := t.new_temp('return_opt')
|
||||
result << t.make_decl_assign_typed(tmp_name, new_expr, expr_type)
|
||||
tmp_ident := t.make_ident(tmp_name)
|
||||
ok_cond := t.make_selector(tmp_ident, 'ok', 'bool')
|
||||
value := t.make_selector(t.make_ident(tmp_name), 'value', t.optional_base_type(expr_type))
|
||||
then_block := t.make_block(arr1(t.make_return(value, ret_type)))
|
||||
else_block := t.make_block(arr1(t.make_none_return_stmt()))
|
||||
result << t.make_if(ok_cond, then_block, else_block)
|
||||
return result
|
||||
}
|
||||
|
||||
fn (t &Transformer) return_expr_is_optional_result(id flat.NodeId) bool {
|
||||
if int(id) < 0 {
|
||||
return false
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .call && !isnil(t.tc) {
|
||||
if name := t.tc.resolved_call_name(id) {
|
||||
if ret := t.tc.fn_ret_types[name] {
|
||||
return t.is_optional_type_name(ret.name())
|
||||
}
|
||||
}
|
||||
return t.is_optional_type_name(t.get_call_return_type(id, node))
|
||||
}
|
||||
if node.kind == .ident {
|
||||
typ := t.var_type(node.value)
|
||||
return t.is_optional_type_name(typ) || t.is_optional_type_name(node.typ)
|
||||
}
|
||||
if node.kind in [.selector, .index, .if_expr, .match_stmt, .or_expr] {
|
||||
return t.is_optional_type_name(t.node_type(id))
|
||||
}
|
||||
return t.is_optional_type_name(node.typ)
|
||||
}
|
||||
|
||||
// return_block_from_branch builds a block that keeps leading statements
|
||||
// (transformed) and turns the tail expression of the branch into a `return`.
|
||||
fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ string) flat.NodeId {
|
||||
branch := t.a.nodes[int(branch_id)]
|
||||
if branch.kind == .return_stmt {
|
||||
mut all := []flat.NodeId{}
|
||||
ret_stmts := t.transform_stmt(branch_id)
|
||||
t.drain_pending(mut all)
|
||||
for stmt in ret_stmts {
|
||||
all << stmt
|
||||
}
|
||||
return t.make_block(all)
|
||||
}
|
||||
if branch.kind != .block {
|
||||
// single expression branch: just `return <expr>`
|
||||
mut all := []flat.NodeId{}
|
||||
ret_val := t.wrap_sum_return_expr(branch_id)
|
||||
t.drain_pending(mut all)
|
||||
all << t.make_return(ret_val, ret_typ)
|
||||
return t.make_block(all)
|
||||
}
|
||||
mut stmt_ids := []flat.NodeId{}
|
||||
for i in 0 .. branch.children_count {
|
||||
stmt_ids << t.a.child(&branch, i)
|
||||
}
|
||||
if stmt_ids.len == 0 {
|
||||
return t.make_block([]flat.NodeId{})
|
||||
}
|
||||
// all but the last are kept as statements (transformed); the last becomes a return
|
||||
lead := stmt_ids[..stmt_ids.len - 1].clone()
|
||||
new_lead := t.transform_stmts(lead)
|
||||
tail_id := stmt_ids[stmt_ids.len - 1]
|
||||
tail := t.a.nodes[int(tail_id)]
|
||||
tail_expr := t.branch_tail_expr(branch_id)
|
||||
mut all := []flat.NodeId{}
|
||||
for s in new_lead {
|
||||
all << s
|
||||
}
|
||||
if tail.kind == .return_stmt {
|
||||
ret_stmts := t.transform_stmt(tail_id)
|
||||
t.drain_pending(mut all)
|
||||
for stmt in ret_stmts {
|
||||
all << stmt
|
||||
}
|
||||
return t.make_block(all)
|
||||
}
|
||||
ret_val := t.wrap_sum_return_expr(tail_expr)
|
||||
t.drain_pending(mut all)
|
||||
ret := t.make_return(ret_val, ret_typ)
|
||||
all << ret
|
||||
return t.make_block(all)
|
||||
}
|
||||
|
||||
// build_return_if_chain recursively converts an if_expr (possibly an else-if
|
||||
// chain) into an if-statement whose branch tails are `return` statements.
|
||||
fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string) flat.NodeId {
|
||||
if_node := t.a.nodes[int(if_id)]
|
||||
cond_id := t.a.child(&if_node, 0)
|
||||
cond_smartcasts := t.extract_all_is_exprs(cond_id)
|
||||
new_cond := t.transform_and_chain_smartcasts(cond_id)
|
||||
then_id := t.a.child(&if_node, 1)
|
||||
for info in cond_smartcasts {
|
||||
t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
|
||||
}
|
||||
then_block := t.return_block_from_branch(then_id, ret_typ)
|
||||
for _ in cond_smartcasts {
|
||||
t.pop_smartcast()
|
||||
}
|
||||
mut else_block := flat.empty_node
|
||||
if if_node.children_count >= 3 {
|
||||
else_id := t.a.child(&if_node, 2)
|
||||
else_node := t.a.nodes[int(else_id)]
|
||||
if else_node.kind == .if_expr {
|
||||
// else-if chain: recurse, wrap resulting if-stmt in a block
|
||||
inner := t.build_return_if_chain(else_id, ret_typ)
|
||||
else_block = t.make_block(arr1(inner))
|
||||
} else {
|
||||
else_block = t.return_block_from_branch(else_id, ret_typ)
|
||||
}
|
||||
}
|
||||
return t.make_if(new_cond, then_block, else_block)
|
||||
}
|
||||
|
||||
// try_expand_return_if detects a `return if cond { a } else { b }` pattern
|
||||
// and expands it into `if cond { return a } else { return b }`.
|
||||
// This simplification makes the C backend's job easier since it avoids
|
||||
// needing statement-expressions for the if-as-value in the return position.
|
||||
//
|
||||
// Leading statements in each branch are preserved, and a trailing `.expr_stmt`
|
||||
// is unwrapped so the inner expression (not the statement) is returned.
|
||||
//
|
||||
// Returns the expanded if-statement as a single-element array, or none if the
|
||||
// pattern does not match. A plain `return if x {..}` with no else (if_expr with
|
||||
// fewer than 3 children) is left unexpanded.
|
||||
fn (mut t Transformer) try_expand_return_if(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
val_id := t.a.child(&node, 0)
|
||||
val_node := t.a.nodes[int(val_id)]
|
||||
if val_node.kind != .if_expr || val_node.children_count < 3 {
|
||||
return none
|
||||
}
|
||||
return arr1(t.build_return_if_chain(val_id, node.typ))
|
||||
}
|
||||
|
||||
fn (mut t Transformer) match_branch_return_block(branch flat.Node, body_start_idx int, ret_typ string) flat.NodeId {
|
||||
mut body_ids := []flat.NodeId{}
|
||||
for i in body_start_idx .. branch.children_count {
|
||||
body_ids << t.a.child(&branch, i)
|
||||
}
|
||||
if body_ids.len == 0 {
|
||||
return t.make_block([]flat.NodeId{})
|
||||
}
|
||||
mut all := []flat.NodeId{}
|
||||
if body_ids.len > 1 {
|
||||
lead := body_ids[..body_ids.len - 1].clone()
|
||||
for stmt in t.transform_stmts(lead) {
|
||||
all << stmt
|
||||
}
|
||||
}
|
||||
tail_id := body_ids[body_ids.len - 1]
|
||||
tail := t.a.nodes[int(tail_id)]
|
||||
if tail.kind == .return_stmt {
|
||||
for stmt in t.transform_stmt(tail_id) {
|
||||
all << stmt
|
||||
}
|
||||
return t.make_block(all)
|
||||
}
|
||||
tail_expr := if tail.kind == .expr_stmt && tail.children_count > 0 {
|
||||
t.a.child(&tail, 0)
|
||||
} else {
|
||||
tail_id
|
||||
}
|
||||
tail_expr_node := t.a.nodes[int(tail_expr)]
|
||||
if tail_expr_node.kind == .match_stmt {
|
||||
nested_return := t.make_return(tail_expr, ret_typ)
|
||||
for stmt in t.transform_stmt(nested_return) {
|
||||
all << stmt
|
||||
}
|
||||
return t.make_block(all)
|
||||
}
|
||||
expected_ret := t.optional_base_type(ret_typ)
|
||||
actual_tail := if tail_expr_node.kind == .enum_val {
|
||||
t.transform_enum_shorthand(tail_expr, tail_expr_node, expected_ret)
|
||||
} else {
|
||||
tail_expr
|
||||
}
|
||||
ret_val := t.wrap_sum_return_expr(actual_tail)
|
||||
t.drain_pending(mut all)
|
||||
all << t.make_return(ret_val, ret_typ)
|
||||
return t.make_block(all)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) build_return_match_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branches []flat.NodeId, idx int, ret_typ string) flat.NodeId {
|
||||
if idx >= branches.len {
|
||||
return t.a.add(flat.NodeKind.empty)
|
||||
}
|
||||
branch := t.a.nodes[int(branches[idx])]
|
||||
is_else := branch.value == 'else'
|
||||
body_start_idx := if is_else { 0 } else { t.count_conds(branch) }
|
||||
if !is_else && t.match_branch_all_type_patterns(branch) && t.count_conds(branch) > 1 {
|
||||
return t.build_return_match_type_branch_chain(match_expr_id, orig_expr_id, branch,
|
||||
branches, idx, 0, ret_typ)
|
||||
}
|
||||
|
||||
mut sc_pushed := 0
|
||||
if !is_else {
|
||||
n_conds := t.count_conds(branch)
|
||||
if n_conds == 1 {
|
||||
cond_val_id := t.a.child(&branch, 0)
|
||||
if variant_name := t.match_type_pattern(cond_val_id) {
|
||||
subj := t.expr_key(match_expr_id)
|
||||
sum_name := t.find_sum_type_for_variant(variant_name)
|
||||
if subj.len > 0 && sum_name.len > 0 {
|
||||
t.push_smartcast(subj, variant_name, sum_name)
|
||||
sc_pushed++
|
||||
}
|
||||
orig_subj := t.expr_key(orig_expr_id)
|
||||
if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
|
||||
t.push_smartcast(orig_subj, variant_name, sum_name)
|
||||
sc_pushed++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body_block := t.match_branch_return_block(branch, body_start_idx, ret_typ)
|
||||
for _ in 0 .. sc_pushed {
|
||||
t.pop_smartcast()
|
||||
}
|
||||
if is_else {
|
||||
return body_block
|
||||
}
|
||||
|
||||
cond_id := t.build_match_cond(match_expr_id, branch)
|
||||
mut if_ids := []flat.NodeId{}
|
||||
if_ids << cond_id
|
||||
if_ids << body_block
|
||||
if idx + 1 < branches.len {
|
||||
if_ids << t.build_return_match_chain(match_expr_id, orig_expr_id, branches, idx + 1,
|
||||
ret_typ)
|
||||
}
|
||||
if_start := t.a.children.len
|
||||
for id in if_ids {
|
||||
t.a.children << id
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .if_expr
|
||||
children_start: if_start
|
||||
children_count: flat.child_count(if_ids.len)
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) build_return_match_type_branch_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branch flat.Node, branches []flat.NodeId, idx int, cond_idx int, ret_typ string) flat.NodeId {
|
||||
n_conds := t.count_conds(branch)
|
||||
if cond_idx >= n_conds {
|
||||
return if idx + 1 < branches.len {
|
||||
t.build_return_match_chain(match_expr_id, orig_expr_id, branches, idx + 1, ret_typ)
|
||||
} else {
|
||||
t.a.add(flat.NodeKind.empty)
|
||||
}
|
||||
}
|
||||
cond_val_id := t.a.child(&branch, cond_idx)
|
||||
variant_name := t.match_type_pattern(cond_val_id) or {
|
||||
return t.build_return_match_chain(match_expr_id, orig_expr_id, branches, idx + 1, ret_typ)
|
||||
}
|
||||
is_start := t.a.children.len
|
||||
t.a.children << match_expr_id
|
||||
is_id := t.a.add_node(flat.Node{
|
||||
kind: .is_expr
|
||||
value: variant_name
|
||||
children_start: is_start
|
||||
children_count: 1
|
||||
})
|
||||
cond_id := t.transform_is_expr(is_id, t.a.nodes[int(is_id)])
|
||||
|
||||
mut sc_pushed := 0
|
||||
subj := t.expr_key(match_expr_id)
|
||||
sum_name := t.find_sum_type_for_variant(variant_name)
|
||||
if subj.len > 0 && sum_name.len > 0 {
|
||||
t.push_smartcast(subj, variant_name, sum_name)
|
||||
sc_pushed++
|
||||
}
|
||||
orig_subj := t.expr_key(orig_expr_id)
|
||||
if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
|
||||
t.push_smartcast(orig_subj, variant_name, sum_name)
|
||||
sc_pushed++
|
||||
}
|
||||
body_block := t.match_branch_return_block(branch, n_conds, ret_typ)
|
||||
for _ in 0 .. sc_pushed {
|
||||
t.pop_smartcast()
|
||||
}
|
||||
|
||||
else_part := t.build_return_match_type_branch_chain(match_expr_id, orig_expr_id, branch,
|
||||
branches, idx, cond_idx + 1, ret_typ)
|
||||
start := t.a.children.len
|
||||
t.a.children << cond_id
|
||||
t.a.children << body_block
|
||||
t.a.children << else_part
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .if_expr
|
||||
children_start: start
|
||||
children_count: 3
|
||||
})
|
||||
}
|
||||
|
||||
// try_expand_return_match detects a `return match x { ... }` pattern and expands
|
||||
// it into an if/else-if chain where every branch tail is an explicit return.
|
||||
fn (mut t Transformer) try_expand_return_match(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
val_id := t.a.child(&node, 0)
|
||||
val := t.a.nodes[int(val_id)]
|
||||
if val.kind != .match_stmt || val.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
match_expr_id := t.a.child(&val, 0)
|
||||
match_expr := t.a.nodes[int(match_expr_id)]
|
||||
needs_temp := match_expr.kind !in [.ident, .int_literal, .bool_literal, .string_literal,
|
||||
.char_literal]
|
||||
|
||||
mut result := []flat.NodeId{}
|
||||
mut actual_expr_id := flat.empty_node
|
||||
if needs_temp {
|
||||
tmp_name := t.new_temp('match')
|
||||
match_type := t.node_type(match_expr_id)
|
||||
new_expr := t.transform_expr(match_expr_id)
|
||||
t.drain_pending(mut result)
|
||||
result << t.make_decl_assign_typed(tmp_name, new_expr, match_type)
|
||||
actual_expr_id = t.make_ident(tmp_name)
|
||||
t.a.nodes[int(actual_expr_id)].typ = match_type
|
||||
t.set_var_type(tmp_name, match_type)
|
||||
} else {
|
||||
actual_expr_id = t.transform_expr(match_expr_id)
|
||||
t.drain_pending(mut result)
|
||||
}
|
||||
|
||||
mut branches := []flat.NodeId{}
|
||||
for i in 1 .. val.children_count {
|
||||
branches << t.a.child(&val, i)
|
||||
}
|
||||
result << t.build_return_match_chain(actual_expr_id, match_expr_id, branches, 0, node.typ)
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,571 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
fn (mut t Transformer) transform_field_init_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
val_id := t.a.child(&node, 0)
|
||||
val_node := t.a.nodes[int(val_id)]
|
||||
new_val := if inferred_sum := t.sum_type_for_field_variant(node.value, val_id, val_node) {
|
||||
t.wrap_sum_value(val_id, inferred_sum)
|
||||
} else {
|
||||
t.transform_expr(val_id)
|
||||
}
|
||||
start := t.a.children.len
|
||||
t.a.children << new_val
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .field_init
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: 1
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
|
||||
// transform_struct_fields transforms struct initialization fields with enum resolution.
|
||||
// For each .field_init child, transforms the value expression. If the struct field type
|
||||
// is a known enum, resolves shorthand enum values (e.g. `.red` -> `Color.red`).
|
||||
fn (mut t Transformer) transform_struct_fields(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return t.add_missing_struct_defaults(id, node)
|
||||
}
|
||||
info := t.lookup_struct_info(node.value) or {
|
||||
// Unknown struct: fall back to generic child transform
|
||||
return t.transform_struct_children(id, node)
|
||||
}
|
||||
// Build a field name -> type lookup from the struct definition
|
||||
mut field_types := map[string]string{}
|
||||
mut field_order := []string{cap: info.fields.len}
|
||||
for f in info.fields {
|
||||
field_types[f.name] = f.typ
|
||||
field_order << f.name
|
||||
}
|
||||
mut field_ids := []flat.NodeId{}
|
||||
mut promoted_fields := map[string][]flat.NodeId{}
|
||||
mut promoted_types := map[string]string{}
|
||||
mut prelude := []flat.NodeId{}
|
||||
t.drain_pending(mut prelude)
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind == .field_init && child.children_count > 0 {
|
||||
val_id := t.a.child(&child, 0)
|
||||
val_node := t.a.nodes[int(val_id)]
|
||||
field_name := if child.value.len > 0 {
|
||||
child.value
|
||||
} else if i < field_order.len {
|
||||
field_order[i]
|
||||
} else {
|
||||
''
|
||||
}
|
||||
mut target_field_name := field_name
|
||||
mut field_type := field_types[field_name] or { '' }
|
||||
mut promoted_parent := ''
|
||||
if field_type.len == 0 {
|
||||
if embedded := t.embedded_field_for_promoted_field(info, field_name) {
|
||||
promoted_parent = embedded.name
|
||||
promoted_types[promoted_parent] = embedded.typ
|
||||
if embedded_info := t.lookup_struct_info(embedded.typ) {
|
||||
if promoted_type := t.struct_field_type(embedded_info, field_name) {
|
||||
field_type = promoted_type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if val_node.kind == .array_literal && is_fixed_array_type(field_type) {
|
||||
t.a.nodes[int(val_id)].typ = field_type
|
||||
}
|
||||
value_type := t.node_type(val_id)
|
||||
sum_field_type := t.struct_field_sum_type(field_type, info.module)
|
||||
enum_field_type := t.enum_type_name_for_expected(field_type, info.module)
|
||||
// Check if the value is an enum shorthand and the field type is an enum
|
||||
mut new_val := if val_node.kind == .enum_val && enum_field_type.len > 0 {
|
||||
t.transform_enum_shorthand(val_id, val_node, enum_field_type)
|
||||
} else if field_type.starts_with('[]') && is_fixed_array_type(value_type) {
|
||||
t.fixed_array_value_to_array(val_id, value_type, field_type)
|
||||
} else if sum_field_type.len > 0 {
|
||||
t.wrap_sum_value(val_id, sum_field_type)
|
||||
} else if inferred_sum := t.sum_type_for_field_variant(field_name, val_id, val_node) {
|
||||
t.wrap_sum_value(val_id, inferred_sum)
|
||||
} else if field_type.starts_with('&') {
|
||||
t.transform_expr_for_type(val_id, field_type)
|
||||
} else if field_type.len > 0 {
|
||||
t.transform_expr_for_type(val_id, field_type)
|
||||
} else {
|
||||
t.transform_expr(val_id)
|
||||
}
|
||||
if sum_field_type.len == 0 && field_type.len > 0 {
|
||||
new_val = t.coerce_transformed_expr_to_type(new_val, val_id, field_type)
|
||||
}
|
||||
t.drain_pending(mut prelude)
|
||||
fi_start := t.a.children.len
|
||||
t.a.children << new_val
|
||||
new_field := t.a.add_node(flat.Node{
|
||||
kind: .field_init
|
||||
op: child.op
|
||||
children_start: fi_start
|
||||
children_count: 1
|
||||
pos: child.pos
|
||||
value: target_field_name
|
||||
typ: child.typ
|
||||
})
|
||||
if promoted_parent.len > 0 {
|
||||
mut promoted := promoted_fields[promoted_parent] or { []flat.NodeId{} }
|
||||
promoted << new_field
|
||||
promoted_fields[promoted_parent] = promoted
|
||||
} else {
|
||||
field_ids << new_field
|
||||
}
|
||||
} else {
|
||||
field_ids << child_id
|
||||
}
|
||||
}
|
||||
for parent, promoted in promoted_fields {
|
||||
promoted_start := t.a.children.len
|
||||
for fid in promoted {
|
||||
t.a.children << fid
|
||||
}
|
||||
embedded_type := promoted_types[parent] or { parent }
|
||||
embedded_init := t.a.add_node(flat.Node{
|
||||
kind: .struct_init
|
||||
children_start: promoted_start
|
||||
children_count: flat.child_count(promoted.len)
|
||||
value: embedded_type
|
||||
typ: embedded_type
|
||||
})
|
||||
fi_start := t.a.children.len
|
||||
t.a.children << embedded_init
|
||||
field_ids << t.a.add_node(flat.Node{
|
||||
kind: .field_init
|
||||
children_start: fi_start
|
||||
children_count: 1
|
||||
value: parent
|
||||
typ: embedded_type
|
||||
})
|
||||
}
|
||||
start := t.a.children.len
|
||||
for fid in field_ids {
|
||||
t.a.children << fid
|
||||
}
|
||||
new_id := t.a.add_node(flat.Node{
|
||||
kind: .struct_init
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(field_ids.len)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
final_id := t.add_missing_struct_defaults(new_id, t.a.nodes[int(new_id)])
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
return final_id
|
||||
}
|
||||
|
||||
// transform_struct_children is a fallback for struct inits where the struct type is unknown.
|
||||
// Transforms all field_init value expressions without enum resolution.
|
||||
fn (mut t Transformer) transform_struct_children(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut field_ids := []flat.NodeId{}
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind == .field_init && child.children_count > 0 {
|
||||
val_id := t.a.child(&child, 0)
|
||||
new_val := t.transform_expr(val_id)
|
||||
fi_start := t.a.children.len
|
||||
t.a.children << new_val
|
||||
field_ids << t.a.add_node(flat.Node{
|
||||
kind: .field_init
|
||||
op: child.op
|
||||
children_start: fi_start
|
||||
children_count: 1
|
||||
pos: child.pos
|
||||
value: child.value
|
||||
typ: child.typ
|
||||
})
|
||||
} else {
|
||||
field_ids << child_id
|
||||
}
|
||||
}
|
||||
start := t.a.children.len
|
||||
for fid in field_ids {
|
||||
t.a.children << fid
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .struct_init
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(field_ids.len)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
|
||||
// add_missing_struct_defaults checks if any fields with default values are missing
|
||||
// from the struct initialization. This is a hook point for future default-fill logic.
|
||||
// Currently returns the node unchanged because StructInfo does not yet store default values.
|
||||
fn (mut t Transformer) add_missing_struct_defaults(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.value.len == 0 {
|
||||
return id
|
||||
}
|
||||
info := t.lookup_struct_info(node.value) or { return id }
|
||||
mut provided := map[string]bool{}
|
||||
mut field_ids := []flat.NodeId{cap: int(node.children_count) + info.fields.len}
|
||||
mut prelude := []flat.NodeId{}
|
||||
t.drain_pending(mut prelude)
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind == .field_init {
|
||||
provided[child.value] = true
|
||||
}
|
||||
field_ids << child_id
|
||||
}
|
||||
old_module := t.cur_module
|
||||
if info.module.len > 0 {
|
||||
t.cur_module = info.module
|
||||
}
|
||||
mut added := false
|
||||
for field in info.fields {
|
||||
if field.name in provided || int(field.default_expr) < 0 {
|
||||
continue
|
||||
}
|
||||
default_node := t.a.nodes[int(field.default_expr)]
|
||||
enum_field_type := t.enum_type_name_for_expected(field.typ, info.module)
|
||||
new_val := if default_node.kind == .enum_val && enum_field_type.len > 0 {
|
||||
t.transform_enum_shorthand(field.default_expr, default_node, enum_field_type)
|
||||
} else if t.is_sum_type_name(field.typ) {
|
||||
// A sum-type field default (e.g. `typ_expr Expr = EmptyExpr{}`) must be
|
||||
// wrapped into the sum, not emitted as the bare variant. wrap_sum_value
|
||||
// is a no-op when the value already is the sum type.
|
||||
t.wrap_sum_value(field.default_expr, field.typ)
|
||||
} else {
|
||||
t.transform_expr_for_type(field.default_expr, field.typ)
|
||||
}
|
||||
t.drain_pending(mut prelude)
|
||||
fi_start := t.a.children.len
|
||||
t.a.children << new_val
|
||||
field_ids << t.a.add_node(flat.Node{
|
||||
kind: .field_init
|
||||
children_start: fi_start
|
||||
children_count: 1
|
||||
value: field.name
|
||||
typ: field.typ
|
||||
})
|
||||
provided[field.name] = true
|
||||
added = true
|
||||
}
|
||||
t.cur_module = old_module
|
||||
if !added {
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
return id
|
||||
}
|
||||
start := t.a.children.len
|
||||
for fid in field_ids {
|
||||
t.a.children << fid
|
||||
}
|
||||
final_id := t.a.add_node(flat.Node{
|
||||
kind: .struct_init
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: flat.child_count(field_ids.len)
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
return final_id
|
||||
}
|
||||
|
||||
fn (t &Transformer) lookup_struct_info(name string) ?StructInfo {
|
||||
if name.contains('.') {
|
||||
if name in t.structs {
|
||||
return t.structs[name]
|
||||
}
|
||||
} else if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${name}'
|
||||
if qname in t.structs {
|
||||
return t.structs[qname]
|
||||
}
|
||||
}
|
||||
if name in t.structs {
|
||||
return t.structs[name]
|
||||
}
|
||||
if name.contains('.') {
|
||||
short_name := name.all_after_last('.')
|
||||
if short_name in t.structs {
|
||||
return t.structs[short_name]
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) struct_field_type(info StructInfo, field_name string) ?string {
|
||||
for field in info.fields {
|
||||
if field.name == field_name {
|
||||
return field.typ
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) embedded_field_for_promoted_field(info StructInfo, field_name string) ?FieldInfo {
|
||||
for field in info.fields {
|
||||
if !t.is_embedded_field(field) {
|
||||
continue
|
||||
}
|
||||
embedded_info := t.lookup_struct_info(field.typ) or { continue }
|
||||
if _ := t.struct_field_type(embedded_info, field_name) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) is_embedded_field(field FieldInfo) bool {
|
||||
if field.name.len == 0 || field.typ.len == 0 {
|
||||
return false
|
||||
}
|
||||
short_typ := if field.typ.contains('.') { field.typ.all_after_last('.') } else { field.typ }
|
||||
short_name := if field.name.contains('.') { field.name.all_after_last('.') } else { field.name }
|
||||
return short_name == short_typ
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_assoc_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.kind != .assoc || node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
base_id := t.a.child(&node, 0)
|
||||
base_type := t.node_type(base_id)
|
||||
mut assoc_type := node.value
|
||||
if assoc_type.len == 0 {
|
||||
assoc_type = base_type
|
||||
}
|
||||
if assoc_type.starts_with('&') {
|
||||
assoc_type = assoc_type[1..]
|
||||
}
|
||||
mut field_types := map[string]string{}
|
||||
mut assoc_module := ''
|
||||
if info := t.lookup_struct_info(assoc_type) {
|
||||
assoc_module = info.module
|
||||
for field in info.fields {
|
||||
field_types[field.name] = field.typ
|
||||
}
|
||||
}
|
||||
|
||||
tmp_name := t.new_temp('assoc')
|
||||
outer_pending := t.pending_stmts.clone()
|
||||
t.pending_stmts.clear()
|
||||
|
||||
mut prelude := []flat.NodeId{}
|
||||
transformed_base := t.transform_expr(base_id)
|
||||
base := if base_type.starts_with('&') {
|
||||
t.make_prefix(.mul, transformed_base)
|
||||
} else {
|
||||
transformed_base
|
||||
}
|
||||
t.drain_pending(mut prelude)
|
||||
prelude << t.make_decl_assign_typed(tmp_name, base, assoc_type)
|
||||
|
||||
for i in 1 .. node.children_count {
|
||||
field_id := t.a.child(&node, i)
|
||||
field := t.a.nodes[int(field_id)]
|
||||
if field.kind != .field_init || field.children_count == 0 {
|
||||
continue
|
||||
}
|
||||
value_id := t.a.child(&field, 0)
|
||||
value_node := t.a.nodes[int(value_id)]
|
||||
field_type := field_types[field.value] or { '' }
|
||||
if value_node.kind == .array_literal && is_fixed_array_type(field_type) {
|
||||
t.a.nodes[int(value_id)].typ = field_type
|
||||
}
|
||||
value_type := t.node_type(value_id)
|
||||
enum_field_type := t.enum_type_name_for_expected(field_type, assoc_module)
|
||||
value := if value_node.kind == .enum_val && enum_field_type.len > 0 {
|
||||
t.transform_enum_shorthand(value_id, value_node, enum_field_type)
|
||||
} else if field_type.starts_with('[]') && is_fixed_array_type(value_type) {
|
||||
t.fixed_array_value_to_array(value_id, value_type, field_type)
|
||||
} else if t.is_sum_type_name(field_type) {
|
||||
t.wrap_sum_value(value_id, field_type)
|
||||
} else if field_type.len > 0 {
|
||||
t.transform_expr_for_type(value_id, field_type)
|
||||
} else {
|
||||
t.transform_expr(value_id)
|
||||
}
|
||||
t.drain_pending(mut prelude)
|
||||
prelude << t.make_assign(t.make_selector(t.make_ident(tmp_name), field.value, field_type),
|
||||
value)
|
||||
}
|
||||
|
||||
t.pending_stmts = outer_pending
|
||||
for stmt in prelude {
|
||||
t.pending_stmts << stmt
|
||||
}
|
||||
result := t.make_ident(tmp_name)
|
||||
t.a.nodes[int(result)].typ = assoc_type
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_amp_assoc_expr_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
|
||||
if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
|
||||
return none
|
||||
}
|
||||
child_id := t.a.child(&node, 0)
|
||||
child := t.a.nodes[int(child_id)]
|
||||
if child.kind != .assoc {
|
||||
return none
|
||||
}
|
||||
value := t.transform_assoc_expr(child_id, child)
|
||||
mut value_type := t.node_type(value)
|
||||
if value_type.len == 0 {
|
||||
value_type = t.node_type(child_id)
|
||||
}
|
||||
if value_type.starts_with('&') {
|
||||
value_type = value_type[1..]
|
||||
}
|
||||
if value_type.len == 0 {
|
||||
return none
|
||||
}
|
||||
addr := t.make_prefix(.amp, value)
|
||||
size := t.make_sizeof_type(value_type)
|
||||
dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
|
||||
mut ptr_type := target_type
|
||||
if ptr_type.len == 0 {
|
||||
ptr_type = node.typ
|
||||
}
|
||||
if !ptr_type.starts_with('&') {
|
||||
ptr_type = '&${value_type}'
|
||||
}
|
||||
cast := t.make_cast(ptr_type, dup, ptr_type)
|
||||
t.a.nodes[int(cast)].typ = ptr_type
|
||||
return cast
|
||||
}
|
||||
|
||||
fn (t &Transformer) struct_field_sum_type(field_type string, owner_module string) string {
|
||||
if t.is_sum_type_name(field_type) {
|
||||
return field_type
|
||||
}
|
||||
if field_type.len == 0 || field_type.contains('.') || owner_module.len == 0 {
|
||||
return ''
|
||||
}
|
||||
qname := '${owner_module}.${field_type}'
|
||||
if qname in t.sum_types {
|
||||
return qname
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (t &Transformer) sum_type_for_field_variant(field_name string, val_id flat.NodeId, val_node flat.Node) ?string {
|
||||
if field_name != 'info' {
|
||||
return none
|
||||
}
|
||||
mut variant := if val_node.kind in [.struct_init, .cast_expr, .assoc] && val_node.value.len > 0 {
|
||||
val_node.value
|
||||
} else if val_node.kind == .assoc && val_node.children_count > 0 {
|
||||
t.node_type(t.a.child(&val_node, 0))
|
||||
} else {
|
||||
t.node_type(val_id)
|
||||
}
|
||||
if variant.starts_with('&') {
|
||||
variant = variant[1..]
|
||||
}
|
||||
if variant.len == 0 {
|
||||
return none
|
||||
}
|
||||
short_variant := if variant.contains('.') { variant.all_after_last('.') } else { variant }
|
||||
for sum_name, variants in t.sum_types {
|
||||
if sum_name != 'TypeInfo' && !sum_name.ends_with('.TypeInfo') {
|
||||
continue
|
||||
}
|
||||
for v in variants {
|
||||
short_v := if v.contains('.') { v.all_after_last('.') } else { v }
|
||||
if v == variant || short_v == short_variant {
|
||||
return sum_name
|
||||
}
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut t Transformer) fixed_array_value_to_array(value_id flat.NodeId, fixed_type string, array_type string) flat.NodeId {
|
||||
elem_type := fixed_array_elem_type(fixed_type)
|
||||
len_expr := t.make_fixed_array_len_expr(fixed_type)
|
||||
return t.make_call_typed('new_array_from_c_array', [
|
||||
len_expr,
|
||||
len_expr,
|
||||
t.make_sizeof_type(elem_type),
|
||||
t.transform_expr(value_id),
|
||||
], array_type)
|
||||
}
|
||||
|
||||
// transform_array_init_expr transforms .array_init nodes (e.g. `[]int{len: n}`).
|
||||
// Recursively transforms any child expressions (len, cap, init values).
|
||||
fn (mut t Transformer) transform_array_init_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
lowered := t.lower_array_init_to_runtime(id, node)
|
||||
if lowered != id {
|
||||
return lowered
|
||||
}
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
new_children << t.transform_expr(child_id)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for nc in new_children {
|
||||
t.a.children << nc
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .array_init
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: node.children_count
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
|
||||
// transform_map_init_expr transforms .map_init nodes.
|
||||
// Recursively transforms all child key/value expressions.
|
||||
fn (mut t Transformer) transform_map_init_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.value.starts_with('map[') || node.typ.starts_with('map[') {
|
||||
return t.lower_map_init_to_runtime(id, node)
|
||||
}
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
||||
for i in 0 .. node.children_count {
|
||||
child_id := t.a.child(&node, i)
|
||||
new_children << t.transform_expr(child_id)
|
||||
}
|
||||
start := t.a.children.len
|
||||
for nc in new_children {
|
||||
t.a.children << nc
|
||||
}
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .map_init
|
||||
op: node.op
|
||||
children_start: start
|
||||
children_count: node.children_count
|
||||
pos: node.pos
|
||||
value: node.value
|
||||
typ: node.typ
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,533 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
fn (t &Transformer) trim_pointer_type(typ string) string {
|
||||
if typ.starts_with('&') {
|
||||
return typ[1..]
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (t &Transformer) resolve_variant(sum_name string, variant string) string {
|
||||
if variant.contains('.') {
|
||||
return variant
|
||||
}
|
||||
resolved_sum := t.resolve_sum_name(sum_name)
|
||||
if resolved_variant := t.sum_variant_name(resolved_sum, variant) {
|
||||
return resolved_variant
|
||||
}
|
||||
if resolved_sum.contains('.') {
|
||||
return '${resolved_sum.all_before_last('.')}.${variant}'
|
||||
}
|
||||
if sum_name.contains('.') {
|
||||
return '${sum_name.all_before_last('.')}.${variant}'
|
||||
}
|
||||
return variant
|
||||
}
|
||||
|
||||
fn (t &Transformer) resolve_sum_name(sum_name string) string {
|
||||
if sum_name.len == 0 {
|
||||
return sum_name
|
||||
}
|
||||
if isnil(t.sum_cache) {
|
||||
return t.resolve_sum_name_uncached(sum_name)
|
||||
}
|
||||
mut c := t.sum_cache
|
||||
if c.module != t.cur_module {
|
||||
c.module = t.cur_module
|
||||
c.entries.clear()
|
||||
}
|
||||
if cached := c.entries[sum_name] {
|
||||
return cached
|
||||
}
|
||||
result := t.resolve_sum_name_uncached(sum_name)
|
||||
c.entries[sum_name] = result
|
||||
return result
|
||||
}
|
||||
|
||||
fn (t &Transformer) resolve_sum_name_uncached(sum_name string) string {
|
||||
if sum_name in t.sum_types {
|
||||
return sum_name
|
||||
}
|
||||
if sum_name.contains('.') {
|
||||
short_sum := sum_name.all_after_last('.')
|
||||
if short_sum in t.sum_types {
|
||||
return short_sum
|
||||
}
|
||||
}
|
||||
if !sum_name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qsum := '${t.cur_module}.${sum_name}'
|
||||
if qsum in t.sum_types {
|
||||
return qsum
|
||||
}
|
||||
}
|
||||
if !sum_name.contains('.') {
|
||||
mut found := ''
|
||||
for key, _ in t.sum_types {
|
||||
if key.contains('.') && key.all_after_last('.') == sum_name {
|
||||
if found.len > 0 && found != key {
|
||||
found = ''
|
||||
break
|
||||
}
|
||||
found = key
|
||||
}
|
||||
}
|
||||
if found.len > 0 {
|
||||
return found
|
||||
}
|
||||
}
|
||||
if !isnil(t.tc) {
|
||||
if sum_name in t.tc.sum_types {
|
||||
return sum_name
|
||||
}
|
||||
if sum_name.contains('.') {
|
||||
short_sum := sum_name.all_after_last('.')
|
||||
if short_sum in t.tc.sum_types {
|
||||
return short_sum
|
||||
}
|
||||
}
|
||||
if !sum_name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qsum := '${t.cur_module}.${sum_name}'
|
||||
if qsum in t.tc.sum_types {
|
||||
return qsum
|
||||
}
|
||||
}
|
||||
if !sum_name.contains('.') {
|
||||
mut found := ''
|
||||
for key, _ in t.tc.sum_types {
|
||||
if key.contains('.') && key.all_after_last('.') == sum_name {
|
||||
if found.len > 0 && found != key {
|
||||
found = ''
|
||||
break
|
||||
}
|
||||
found = key
|
||||
}
|
||||
}
|
||||
if found.len > 0 {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum_name
|
||||
}
|
||||
|
||||
fn (t &Transformer) is_sum_type_name(name string) bool {
|
||||
if name.len == 0 {
|
||||
return false
|
||||
}
|
||||
resolved := t.resolve_sum_name(name)
|
||||
return resolved in t.sum_types
|
||||
}
|
||||
|
||||
fn (t &Transformer) is_interface_type_name(name string) bool {
|
||||
if name.len == 0 || isnil(t.tc) {
|
||||
return false
|
||||
}
|
||||
clean := t.trim_pointer_type(t.normalize_type_alias(name))
|
||||
if clean == 'IError' || clean in t.tc.interface_names {
|
||||
return true
|
||||
}
|
||||
if !clean.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
return '${t.cur_module}.${clean}' in t.tc.interface_names
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (t &Transformer) interface_variant_type(variant string) string {
|
||||
if variant.contains('.') {
|
||||
return variant
|
||||
}
|
||||
if variant in t.structs {
|
||||
return variant
|
||||
}
|
||||
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qvariant := '${t.cur_module}.${variant}'
|
||||
if qvariant in t.structs {
|
||||
return qvariant
|
||||
}
|
||||
}
|
||||
for name, _ in t.structs {
|
||||
if name.contains('.') && name.all_after_last('.') == variant {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return variant
|
||||
}
|
||||
|
||||
fn (t &Transformer) smartcast_target_type(sc SmartcastContext) string {
|
||||
if t.is_interface_type_name(sc.sum_type_name) {
|
||||
return t.interface_variant_type(sc.variant_name)
|
||||
}
|
||||
return t.resolve_variant(sc.sum_type_name, sc.variant_name)
|
||||
}
|
||||
|
||||
fn (t &Transformer) sum_type_index(sum_name string, variant string) int {
|
||||
resolved_sum := t.resolve_sum_name(sum_name)
|
||||
variants := t.sum_types[resolved_sum] or {
|
||||
if !isnil(t.tc) {
|
||||
tc_variants := t.tc.sum_types[resolved_sum] or { return 0 }
|
||||
return sum_type_index_in_variants(tc_variants, variant)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return sum_type_index_in_variants(variants, variant)
|
||||
}
|
||||
|
||||
fn sum_type_index_in_variants(variants []string, variant string) int {
|
||||
short_variant := if variant.contains('.') { variant.all_after_last('.') } else { variant }
|
||||
for i, v in variants {
|
||||
short_v := if v.contains('.') { v.all_after_last('.') } else { v }
|
||||
if v == variant || short_v == short_variant {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_is_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
expr_id := t.a.child(&node, 0)
|
||||
expr_type := t.node_type(expr_id)
|
||||
clean_type0 := t.trim_pointer_type(expr_type)
|
||||
clean_type := if clean_type0 in t.sum_types {
|
||||
clean_type0
|
||||
} else {
|
||||
t.find_sum_type_for_variant(node.value)
|
||||
}
|
||||
if t.is_interface_type_name(clean_type0) {
|
||||
new_expr := t.transform_expr(expr_id)
|
||||
object := t.make_selector_op(new_expr, '_object', 'voidptr', if expr_type.starts_with('&') {
|
||||
.arrow
|
||||
} else {
|
||||
.dot
|
||||
})
|
||||
return t.make_infix(.ne, object, t.a.add(.nil_literal))
|
||||
}
|
||||
if clean_type.len == 0 || clean_type !in t.sum_types {
|
||||
return t.make_bool_literal(true)
|
||||
}
|
||||
new_expr := t.transform_expr(expr_id)
|
||||
if check := t.make_sum_type_pattern_check(new_expr, expr_type, clean_type, node.value) {
|
||||
return check
|
||||
}
|
||||
return t.make_bool_literal(true)
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_sum_is_check(expr flat.NodeId, expr_type string, sum_name string, variant string) flat.NodeId {
|
||||
tag := t.make_selector_op(expr, 'typ', 'int', if expr_type.starts_with('&') {
|
||||
.arrow
|
||||
} else {
|
||||
.dot
|
||||
})
|
||||
idx := t.make_int_literal(t.sum_type_index(sum_name, variant))
|
||||
return t.make_infix(.eq, tag, idx)
|
||||
}
|
||||
|
||||
fn (t &Transformer) sum_variant_path(sum_name string, variant string) []string {
|
||||
mut visited := map[string]bool{}
|
||||
return t.sum_variant_path_inner(sum_name, variant, mut visited)
|
||||
}
|
||||
|
||||
fn (t &Transformer) sum_variant_path_inner(sum_name string, variant string, mut visited map[string]bool) []string {
|
||||
resolved_sum := t.resolve_sum_name(t.trim_pointer_type(sum_name))
|
||||
if resolved_sum.len == 0 || resolved_sum in visited {
|
||||
return []string{}
|
||||
}
|
||||
visited[resolved_sum] = true
|
||||
if direct := t.sum_variant_name(resolved_sum, variant) {
|
||||
return [direct]
|
||||
}
|
||||
variants := t.sum_types[resolved_sum] or { return []string{} }
|
||||
for direct in variants {
|
||||
direct_sum := t.resolve_sum_name(t.trim_pointer_type(direct))
|
||||
if direct_sum == resolved_sum || direct_sum !in t.sum_types {
|
||||
continue
|
||||
}
|
||||
nested := t.sum_variant_path_inner(direct_sum, variant, mut visited)
|
||||
if nested.len == 0 {
|
||||
continue
|
||||
}
|
||||
mut path := []string{cap: nested.len + 1}
|
||||
path << direct
|
||||
path << nested
|
||||
return path
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_sum_type_pattern_check(expr flat.NodeId, expr_type string, sum_name string, variant string) ?flat.NodeId {
|
||||
resolved_sum := t.resolve_sum_name(t.trim_pointer_type(sum_name))
|
||||
path := t.sum_variant_path(resolved_sum, variant)
|
||||
if path.len == 0 {
|
||||
return none
|
||||
}
|
||||
mut current := expr
|
||||
mut current_type := expr_type
|
||||
mut current_sum := resolved_sum
|
||||
mut chain := flat.empty_node
|
||||
for i, path_variant in path {
|
||||
cmp := t.make_sum_is_check(current, current_type, current_sum, path_variant)
|
||||
if int(chain) < 0 {
|
||||
chain = cmp
|
||||
} else {
|
||||
chain = t.make_infix(.logical_and, chain, cmp)
|
||||
}
|
||||
if i == path.len - 1 {
|
||||
break
|
||||
}
|
||||
qv := t.resolve_variant(current_sum, path_variant)
|
||||
use_ptr := t.variant_references_sum(qv, current_sum)
|
||||
field_type := if use_ptr { '&${qv}' } else { qv }
|
||||
current = t.make_selector_op(current, t.sum_field_name(qv), field_type, if current_type.starts_with('&') {
|
||||
.arrow
|
||||
} else {
|
||||
.dot
|
||||
})
|
||||
current_type = field_type
|
||||
current_sum = t.resolve_sum_name(t.trim_pointer_type(qv))
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
fn (mut t Transformer) transform_as_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
||||
if node.children_count == 0 {
|
||||
return id
|
||||
}
|
||||
expr_id := t.a.child(&node, 0)
|
||||
expr_type := t.node_type(expr_id)
|
||||
clean_type0 := t.trim_pointer_type(expr_type)
|
||||
clean_type := if clean_type0 in t.sum_types {
|
||||
clean_type0
|
||||
} else {
|
||||
t.find_sum_type_for_variant(node.value)
|
||||
}
|
||||
if clean_type.len == 0 || clean_type !in t.sum_types {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
qv := t.resolve_variant(clean_type, node.value)
|
||||
if qv.len > 0 && t.normalize_type_alias(clean_type0) == t.normalize_type_alias(qv) {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
sc_key := t.expr_key(expr_id)
|
||||
if sc_key.len > 0 {
|
||||
contexts := t.smartcasts_for(sc_key)
|
||||
mut matched_contexts := []SmartcastContext{}
|
||||
for i, sc in contexts {
|
||||
mut sc_variant := sc.variant_name
|
||||
if sc.variant_name.contains('.') {
|
||||
sc_variant = sc.variant_name.all_after_last('.')
|
||||
}
|
||||
mut target_variant := node.value
|
||||
if node.value.contains('.') {
|
||||
target_variant = node.value.all_after_last('.')
|
||||
}
|
||||
if sc_variant == target_variant {
|
||||
end := i + 1
|
||||
matched_contexts = contexts[..end].clone()
|
||||
}
|
||||
}
|
||||
if matched_contexts.len > 0 {
|
||||
base := t.make_plain_expr_for_smartcast(expr_id)
|
||||
return t.apply_smartcast_contexts(base, t.original_expr_type(expr_id), matched_contexts)
|
||||
}
|
||||
}
|
||||
field := t.sum_field_name(qv)
|
||||
new_expr := t.transform_expr(expr_id)
|
||||
use_ptr := t.variant_references_sum(qv, clean_type)
|
||||
field_typ := if use_ptr { '&${qv}' } else { qv }
|
||||
field_sel := t.make_selector_op(new_expr, field, field_typ, if expr_type.starts_with('&') {
|
||||
.arrow
|
||||
} else {
|
||||
.dot
|
||||
})
|
||||
if use_ptr {
|
||||
return t.make_prefix(.mul, field_sel)
|
||||
}
|
||||
return field_sel
|
||||
}
|
||||
|
||||
fn (mut t Transformer) wrap_sum_return_expr(expr_id flat.NodeId) flat.NodeId {
|
||||
was_return_expr := t.in_return_expr
|
||||
t.in_return_expr = true
|
||||
result := if t.cur_fn_ret_type.len == 0 || t.cur_fn_ret_type !in t.sum_types {
|
||||
t.transform_expr(expr_id)
|
||||
} else {
|
||||
t.wrap_sum_value(expr_id, t.cur_fn_ret_type)
|
||||
}
|
||||
t.in_return_expr = was_return_expr
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut t Transformer) wrap_sum_value(expr_id flat.NodeId, target_sum string) flat.NodeId {
|
||||
resolved_sum := t.resolve_sum_name(target_sum)
|
||||
if resolved_sum.len == 0 || resolved_sum !in t.sum_types {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
expr := t.a.nodes[int(expr_id)]
|
||||
if expr.kind == .if_expr {
|
||||
branch_type := t.if_expr_branch_result_type(expr)
|
||||
if t.if_expr_branch_overrides_sum_target(branch_type, resolved_sum) {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
if lowered := t.try_expand_if_expr_value_for_type(expr_id, expr, resolved_sum) {
|
||||
return lowered
|
||||
}
|
||||
}
|
||||
expr_type := t.node_type(expr_id)
|
||||
mut variant := expr_type
|
||||
mut expr_smartcast := SmartcastContext{}
|
||||
key := t.expr_key(expr_id)
|
||||
if key.len > 0 {
|
||||
if sc := t.find_smartcast(key) {
|
||||
expr_smartcast = sc
|
||||
}
|
||||
}
|
||||
has_expr_smartcast := expr_smartcast.expr_name.len > 0
|
||||
if has_expr_smartcast && t.resolve_sum_name(expr_smartcast.sum_type_name) == resolved_sum {
|
||||
variant = t.resolve_variant(expr_smartcast.sum_type_name, expr_smartcast.variant_name)
|
||||
}
|
||||
if expr.kind == .prefix && expr.op == .mul && expr.children_count > 0 {
|
||||
inner_type := t.node_type(t.a.child(&expr, 0))
|
||||
if inner_type.starts_with('&') {
|
||||
variant = inner_type[1..]
|
||||
}
|
||||
}
|
||||
if expr.kind == .struct_init || expr.kind == .cast_expr {
|
||||
variant = expr.value
|
||||
} else if expr.kind == .as_expr && expr.value.len > 0 {
|
||||
variant = expr.value
|
||||
} else if expr.kind == .assoc && expr.value.len > 0 {
|
||||
variant = expr.value
|
||||
} else if expr.kind == .assoc && expr.children_count > 0 {
|
||||
variant = t.node_type(t.a.child(&expr, 0))
|
||||
}
|
||||
if expr.kind !in [.assoc, .as_expr, .prefix] && !has_expr_smartcast
|
||||
&& t.resolve_sum_name(expr_type) == resolved_sum {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
if expr_type.starts_with('&') && t.resolve_sum_name(expr_type[1..]) == resolved_sum {
|
||||
inner := t.transform_expr(expr_id)
|
||||
deref := t.make_prefix(.mul, inner)
|
||||
t.a.nodes[int(deref)].typ = resolved_sum
|
||||
return deref
|
||||
}
|
||||
if variant.len == 0 {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
mut clean_variant := if variant.starts_with('&') { variant[1..] } else { variant }
|
||||
if clean_variant.starts_with('ptr') && clean_variant.len > 3 && clean_variant[3..].contains('.') {
|
||||
clean_variant = clean_variant[3..]
|
||||
}
|
||||
if clean_variant.starts_with('ptr') && clean_variant.len > 3
|
||||
&& clean_variant[3..].contains('__') {
|
||||
clean_variant = clean_variant[3..].replace('__', '.')
|
||||
}
|
||||
short_variant := if clean_variant.contains('.') {
|
||||
clean_variant.all_after_last('.')
|
||||
} else {
|
||||
clean_variant
|
||||
}
|
||||
mut matches := false
|
||||
mut matched_variant := clean_variant
|
||||
for v in t.sum_types[resolved_sum] {
|
||||
short_v := if v.contains('.') { v.all_after_last('.') } else { v }
|
||||
if v == clean_variant || short_v == short_variant {
|
||||
matches = true
|
||||
matched_variant = v
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matches {
|
||||
return t.transform_expr(expr_id)
|
||||
}
|
||||
ref_variant := t.variant_references_sum(matched_variant, resolved_sum)
|
||||
mut pointer_variant_child := flat.empty_node
|
||||
if expr.kind == .prefix && expr.op == .mul && expr.children_count > 0 {
|
||||
pointer_variant_child = t.a.child(&expr, 0)
|
||||
}
|
||||
inner := if int(pointer_variant_child) >= 0 && ref_variant {
|
||||
t.transform_expr(pointer_variant_child)
|
||||
} else {
|
||||
t.transform_expr(expr_id)
|
||||
}
|
||||
if expr_type.starts_with('&') {
|
||||
return t.make_sum_literal(resolved_sum, matched_variant, inner)
|
||||
}
|
||||
if int(pointer_variant_child) >= 0 && ref_variant {
|
||||
return t.make_sum_literal(resolved_sum, matched_variant, inner)
|
||||
}
|
||||
if ref_variant {
|
||||
return t.make_sum_literal(resolved_sum, matched_variant, inner)
|
||||
}
|
||||
start := t.a.children.len
|
||||
t.a.children << inner
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .cast_expr
|
||||
value: resolved_sum
|
||||
children_start: start
|
||||
children_count: 1
|
||||
typ: resolved_sum
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) ensure_sum_variant_ref(value flat.NodeId, variant string) flat.NodeId {
|
||||
mut value_type := t.node_type(value)
|
||||
if value_type.starts_with('&') {
|
||||
return value
|
||||
}
|
||||
clean_variant := if variant.starts_with('&') { variant[1..] } else { variant }
|
||||
if value_type.len == 0 {
|
||||
value_type = clean_variant
|
||||
}
|
||||
if t.expr_can_take_address(value) || t.a.nodes[int(value)].kind == .struct_init {
|
||||
ref := t.make_prefix(.amp, value)
|
||||
t.a.nodes[int(ref)].typ = '&${clean_variant}'
|
||||
return ref
|
||||
}
|
||||
tmp_name := t.new_temp('sum_val')
|
||||
t.pending_stmts << t.make_decl_assign_typed(tmp_name, value, value_type)
|
||||
ref := t.make_prefix(.amp, t.make_ident(tmp_name))
|
||||
t.a.nodes[int(ref)].typ = '&${clean_variant}'
|
||||
return ref
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_sum_literal(sum_name string, variant string, value flat.NodeId) flat.NodeId {
|
||||
qvariant := t.resolve_variant(sum_name, variant)
|
||||
typ_field := t.make_sum_literal_field('typ', t.make_int_literal(t.sum_type_index(sum_name,
|
||||
qvariant)), 'int')
|
||||
raw_value_type := t.node_type(value)
|
||||
value_type := if raw_value_type.starts_with('&') {
|
||||
raw_value_type
|
||||
} else {
|
||||
qvariant
|
||||
}
|
||||
value_field := t.make_sum_literal_field(t.sum_field_name(qvariant), value, value_type)
|
||||
start := t.a.children.len
|
||||
t.a.children << typ_field
|
||||
t.a.children << value_field
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .struct_init
|
||||
children_start: start
|
||||
children_count: 2
|
||||
value: sum_name
|
||||
typ: sum_name
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut t Transformer) make_sum_literal_field(name string, value flat.NodeId, typ string) flat.NodeId {
|
||||
start := t.a.children.len
|
||||
t.a.children << value
|
||||
return t.a.add_node(flat.Node{
|
||||
kind: .field_init
|
||||
children_start: start
|
||||
children_count: 1
|
||||
value: name
|
||||
typ: typ
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,626 @@
|
||||
module transform
|
||||
|
||||
import v3.flat
|
||||
|
||||
// propagate_decl_type infers the declared variable type from a .decl_assign node
|
||||
// and registers it in var_types so downstream transforms can resolve it.
|
||||
fn (mut t Transformer) propagate_decl_type(node flat.Node) {
|
||||
if node.kind != .decl_assign || node.children_count < 2 {
|
||||
return
|
||||
}
|
||||
lhs_id := t.a.child(&node, 0)
|
||||
lhs := t.a.nodes[int(lhs_id)]
|
||||
if lhs.kind != .ident || lhs.value.len == 0 {
|
||||
return
|
||||
}
|
||||
mut typ := ''
|
||||
if node.typ.len > 0 {
|
||||
typ = node.typ
|
||||
} else {
|
||||
rhs_id := t.a.child(&node, 1)
|
||||
typ = t.resolve_expr_type(rhs_id)
|
||||
}
|
||||
if typ.len > 0 {
|
||||
t.set_var_type(lhs.value, typ)
|
||||
}
|
||||
}
|
||||
|
||||
// resolve_selector_type resolves the type of a .selector node (e.g. `obj.field`).
|
||||
// Looks up the base expression type, then finds the field in the struct definition.
|
||||
fn (t &Transformer) resolve_selector_type(node flat.Node) string {
|
||||
if node.kind != .selector || node.children_count == 0 {
|
||||
return ''
|
||||
}
|
||||
base_id := t.a.child(&node, 0)
|
||||
base_node := t.a.nodes[int(base_id)]
|
||||
if base_node.kind == .ident && base_node.value == 'C' {
|
||||
// `C.<name>` references a C symbol (e.g. the `C.stdout &C.FILE` global).
|
||||
// Resolve it via the recorded C global/const type and never fall through to
|
||||
// the V struct field-name heuristics below: an unrelated V struct may have a
|
||||
// field with the same name (e.g. `os.Pipe.stdout`), which would otherwise
|
||||
// mistype `C.stdout` and trigger a bogus auto-reference (`&stdout`).
|
||||
if is_c_int_selector(node.value) {
|
||||
return 'int'
|
||||
}
|
||||
cname := 'C.${node.value}'
|
||||
if gt := t.globals[cname] {
|
||||
if gt.len > 0 {
|
||||
return t.normalize_type_alias(gt)
|
||||
}
|
||||
}
|
||||
if typ := t.const_type_name(cname) {
|
||||
return typ
|
||||
}
|
||||
return ''
|
||||
}
|
||||
field_name := node.value
|
||||
if field_name.len == 0 {
|
||||
return ''
|
||||
}
|
||||
if enum_type := t.enum_type_name_from_expr(base_id) {
|
||||
if fields := t.enum_types[enum_type] {
|
||||
if field_name in fields {
|
||||
return enum_type
|
||||
}
|
||||
}
|
||||
}
|
||||
if type_name := t.qualified_enum_type_selector_name(base_id, field_name) {
|
||||
return type_name
|
||||
}
|
||||
if base_node.kind == .ident {
|
||||
if typ := t.const_type_name('${base_node.value}.${field_name}') {
|
||||
return typ
|
||||
}
|
||||
if fields := t.enum_types[base_node.value] {
|
||||
if field_name in fields {
|
||||
return base_node.value
|
||||
}
|
||||
}
|
||||
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qenum := '${t.cur_module}.${base_node.value}'
|
||||
if fields := t.enum_types[qenum] {
|
||||
if field_name in fields {
|
||||
return qenum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
base_type := t.resolve_expr_type(base_id)
|
||||
if base_type.len == 0 {
|
||||
if ftyp := t.lookup_unique_field_type(field_name) {
|
||||
return ftyp
|
||||
}
|
||||
return ''
|
||||
}
|
||||
base_key := t.expr_key(base_id)
|
||||
if base_key.len > 0 {
|
||||
full_key := '${base_key}.${field_name}'
|
||||
if sc := t.find_smartcast(full_key) {
|
||||
return t.smartcast_target_type(sc)
|
||||
}
|
||||
}
|
||||
if base_key.len > 0 {
|
||||
if sc := t.find_smartcast(base_key) {
|
||||
variant_type := t.qualify_variant(sc.variant_name, sc.sum_type_name)
|
||||
if ftyp := t.lookup_struct_field_type(variant_type, field_name) {
|
||||
return ftyp
|
||||
}
|
||||
if ftyp := t.lookup_struct_field_type(sc.variant_name, field_name) {
|
||||
return ftyp
|
||||
}
|
||||
}
|
||||
}
|
||||
// Strip pointer prefix if present (e.g. "&MyStruct" -> "MyStruct")
|
||||
lookup_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
||||
if ftyp := t.lookup_struct_field_type(lookup_type, field_name) {
|
||||
return ftyp
|
||||
}
|
||||
if info := t.lookup_struct_info(lookup_type) {
|
||||
if embedded := t.embedded_field_for_promoted_field(info, field_name) {
|
||||
if embedded_info := t.lookup_struct_info(embedded.typ) {
|
||||
if ftyp := t.struct_field_type(embedded_info, field_name) {
|
||||
return ftyp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ftyp := t.lookup_unique_field_type(field_name) {
|
||||
return ftyp
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn is_c_int_selector(name string) bool {
|
||||
return name in ['errno', 'EINTR', 'STDOUT_FILENO', 'STDERR_FILENO', 'EINVAL']
|
||||
}
|
||||
|
||||
fn (t &Transformer) selector_expr_name(id flat.NodeId) string {
|
||||
if int(id) < 0 {
|
||||
return ''
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .ident {
|
||||
return node.value
|
||||
}
|
||||
if node.kind == .selector && node.children_count > 0 {
|
||||
base := t.selector_expr_name(t.a.child(&node, 0))
|
||||
if base.len > 0 && node.value.len > 0 {
|
||||
return '${base}.${node.value}'
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (t &Transformer) enum_type_name_from_expr(id flat.NodeId) ?string {
|
||||
name := t.selector_expr_name(id)
|
||||
if name.len == 0 {
|
||||
return none
|
||||
}
|
||||
if name in t.enum_types {
|
||||
return name
|
||||
}
|
||||
if !name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${name}'
|
||||
if qname in t.enum_types {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) qualified_enum_type_selector_name(base_id flat.NodeId, field_name string) ?string {
|
||||
base := t.selector_expr_name(base_id)
|
||||
if base.len == 0 || field_name.len == 0 {
|
||||
return none
|
||||
}
|
||||
name := '${base}.${field_name}'
|
||||
if name in t.enum_types {
|
||||
return name
|
||||
}
|
||||
if !name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${name}'
|
||||
if qname in t.enum_types {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) lookup_struct_field_type(type_name string, field_name string) ?string {
|
||||
lookup := t.lookup_struct_info_for_field(type_name, field_name) or { return none }
|
||||
for f in lookup.info.fields {
|
||||
if f.name == field_name {
|
||||
return t.normalize_field_type(f.typ, lookup.owner_type)
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) lookup_struct_field_raw_type(type_name string, field_name string) ?string {
|
||||
lookup := t.lookup_struct_info_for_field(type_name, field_name) or { return none }
|
||||
for f in lookup.info.fields {
|
||||
if f.name == field_name {
|
||||
if f.raw_typ.len > 0 {
|
||||
return f.raw_typ
|
||||
}
|
||||
return f.typ
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) lookup_struct_info_for_field(type_name string, field_name string) ?StructFieldLookup {
|
||||
if type_name.len == 0 || field_name.len == 0 {
|
||||
return none
|
||||
}
|
||||
mut lookup_type := if type_name.starts_with('&') { type_name[1..] } else { type_name }
|
||||
owner_type := lookup_type
|
||||
if lookup_type !in t.structs && lookup_type.contains('.') {
|
||||
short_type := lookup_type.all_after_last('.')
|
||||
if short_type in t.structs {
|
||||
lookup_type = short_type
|
||||
}
|
||||
}
|
||||
if lookup_type !in t.structs && !lookup_type.contains('.') && t.cur_module.len > 0
|
||||
&& t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qtype := '${t.cur_module}.${lookup_type}'
|
||||
if qtype in t.structs {
|
||||
lookup_type = qtype
|
||||
}
|
||||
}
|
||||
info := t.structs[lookup_type] or { return none }
|
||||
return StructFieldLookup{
|
||||
info: info
|
||||
owner_type: owner_type
|
||||
}
|
||||
}
|
||||
|
||||
fn (t &Transformer) lookup_unique_field_type(field_name string) ?string {
|
||||
match field_name {
|
||||
'name' {
|
||||
return 'string'
|
||||
}
|
||||
'generic_types' {
|
||||
return '[]u32'
|
||||
}
|
||||
'obj' {
|
||||
return t.resolve_sum_name('ScopeObject')
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
if ftyp := t.unique_fields[field_name] {
|
||||
if ftyp.len > 0 {
|
||||
return ftyp
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Transformer) normalize_field_type(typ string, owner_type string) string {
|
||||
if typ.len == 0 {
|
||||
return typ
|
||||
}
|
||||
if typ.starts_with('&') {
|
||||
return '&' + t.normalize_field_type(typ[1..], owner_type)
|
||||
}
|
||||
if typ.starts_with('[]') {
|
||||
return '[]' + t.normalize_field_type(typ[2..], owner_type)
|
||||
}
|
||||
if typ.starts_with('?') {
|
||||
return '?' + t.normalize_field_type(typ[1..], owner_type)
|
||||
}
|
||||
if typ.starts_with('!') {
|
||||
return '!' + t.normalize_field_type(typ[1..], owner_type)
|
||||
}
|
||||
if typ.starts_with('map[') {
|
||||
bracket_end := typ.index(']') or { return t.normalize_type_alias(typ) }
|
||||
key_type := t.normalize_field_type(typ[4..bracket_end], owner_type)
|
||||
value_type := t.normalize_field_type(typ[bracket_end + 1..], owner_type)
|
||||
return 'map[${key_type}]${value_type}'
|
||||
}
|
||||
if typ.starts_with('[') {
|
||||
bracket_end := typ.index(']') or { return t.normalize_type_alias(typ) }
|
||||
return typ[..bracket_end + 1] + t.normalize_field_type(typ[bracket_end + 1..], owner_type)
|
||||
}
|
||||
if typ.contains('.') || !owner_type.contains('.') {
|
||||
return t.normalize_type_alias(typ)
|
||||
}
|
||||
owner_mod := owner_type.all_before_last('.')
|
||||
qtyp := '${owner_mod}.${typ}'
|
||||
if qtyp in t.structs || qtyp in t.sum_types || qtyp in t.enum_types {
|
||||
return t.normalize_type_alias(qtyp)
|
||||
}
|
||||
if !isnil(t.tc) {
|
||||
if qtyp in t.tc.type_aliases {
|
||||
return t.normalize_type_alias(qtyp)
|
||||
}
|
||||
}
|
||||
return t.normalize_type_alias(typ)
|
||||
}
|
||||
|
||||
fn (t &Transformer) normalize_type_alias(typ string) string {
|
||||
if typ.len == 0 || isnil(t.tc) {
|
||||
return typ
|
||||
}
|
||||
if is_plain_builtin_alias_type(typ) {
|
||||
return typ
|
||||
}
|
||||
if isnil(t.alias_cache) {
|
||||
return t.normalize_type_alias_uncached(typ)
|
||||
}
|
||||
mut c := t.alias_cache
|
||||
if c.module != t.cur_module {
|
||||
c.module = t.cur_module
|
||||
c.entries.clear()
|
||||
}
|
||||
if cached := c.entries[typ] {
|
||||
return cached
|
||||
}
|
||||
result := t.normalize_type_alias_uncached(typ)
|
||||
c.entries[typ] = result
|
||||
return result
|
||||
}
|
||||
|
||||
fn (t &Transformer) normalize_type_alias_uncached(typ string) string {
|
||||
if is_generic_placeholder_type_name(typ) {
|
||||
return 'int'
|
||||
}
|
||||
if typ.starts_with('mut ') {
|
||||
return '&' + t.normalize_type_alias(typ[4..])
|
||||
}
|
||||
if typ.starts_with('&') {
|
||||
return '&' + t.normalize_type_alias(typ[1..])
|
||||
}
|
||||
if typ.starts_with('[]') {
|
||||
return '[]' + t.normalize_type_alias(typ[2..])
|
||||
}
|
||||
if typ.starts_with('?') {
|
||||
return '?' + t.normalize_type_alias(typ[1..])
|
||||
}
|
||||
if typ.starts_with('!') {
|
||||
return '!' + t.normalize_type_alias(typ[1..])
|
||||
}
|
||||
if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qtyp := '${t.cur_module}.${typ}'
|
||||
if qtyp in t.structs || qtyp in t.sum_types || qtyp in t.enum_types {
|
||||
return qtyp
|
||||
}
|
||||
}
|
||||
if typ in t.structs || typ in t.sum_types || typ in t.enum_types {
|
||||
return typ
|
||||
}
|
||||
if target := t.tc.type_aliases[typ] {
|
||||
return target
|
||||
}
|
||||
if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
||||
&& t.cur_module != 'builtin' {
|
||||
qtyp := '${t.cur_module}.${typ}'
|
||||
if target := t.tc.type_aliases[qtyp] {
|
||||
return target
|
||||
}
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn is_plain_builtin_alias_type(typ string) bool {
|
||||
return match typ {
|
||||
'bool', 'string', 'void', 'int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64',
|
||||
'f32', 'f64', 'rune', 'isize', 'usize', 'voidptr', 'byteptr', 'charptr' {
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_generic_placeholder_type_name(typ string) bool {
|
||||
if typ.contains('.') {
|
||||
return is_generic_placeholder_type_name(typ.all_after_last('.'))
|
||||
}
|
||||
return typ in ['T', 'U', 'V', 'K']
|
||||
}
|
||||
|
||||
fn (t &Transformer) normalize_type_in_module(typ string, mod string) string {
|
||||
clean := typ.trim_space()
|
||||
if clean.len == 0 {
|
||||
return clean
|
||||
}
|
||||
if clean.starts_with('&') {
|
||||
return '&' + t.normalize_type_in_module(clean[1..], mod)
|
||||
}
|
||||
if clean.starts_with('mut ') {
|
||||
return '&' + t.normalize_type_in_module(clean[4..], mod)
|
||||
}
|
||||
if clean.starts_with('shared ') {
|
||||
return 'shared ' + t.normalize_type_in_module(clean[7..], mod)
|
||||
}
|
||||
if clean.starts_with('atomic ') {
|
||||
return 'atomic ' + t.normalize_type_in_module(clean[7..], mod)
|
||||
}
|
||||
if clean.starts_with('?') {
|
||||
return '?' + t.normalize_type_in_module(clean[1..], mod)
|
||||
}
|
||||
if clean.starts_with('!') {
|
||||
return '!' + t.normalize_type_in_module(clean[1..], mod)
|
||||
}
|
||||
if clean.starts_with('...') {
|
||||
return '...' + t.normalize_type_in_module(clean[3..], mod)
|
||||
}
|
||||
if clean.starts_with('[]') {
|
||||
return '[]' + t.normalize_type_in_module(clean[2..], mod)
|
||||
}
|
||||
if clean.contains('.') || mod.len == 0 || mod == 'main' || mod == 'builtin' {
|
||||
return t.normalize_type_alias(clean)
|
||||
}
|
||||
qtyp := '${mod}.${clean}'
|
||||
if qtyp in t.structs || qtyp in t.sum_types || qtyp in t.enum_types {
|
||||
return t.normalize_type_alias(qtyp)
|
||||
}
|
||||
if !isnil(t.tc) {
|
||||
if qtyp in t.tc.type_aliases {
|
||||
return t.normalize_type_alias(qtyp)
|
||||
}
|
||||
}
|
||||
return t.normalize_type_alias(clean)
|
||||
}
|
||||
|
||||
// resolve_index_elem_type determines the element type of an .index expression.
|
||||
// For `[]T` arrays returns `T`, for `map[K]V` returns `V`, for `string` returns `u8`.
|
||||
fn (t &Transformer) resolve_index_elem_type(node flat.Node) string {
|
||||
if node.kind != .index || node.children_count == 0 {
|
||||
return ''
|
||||
}
|
||||
base_id := t.a.child(&node, 0)
|
||||
mut base_type := t.resolve_expr_type(base_id)
|
||||
if base_type.len == 0 {
|
||||
return ''
|
||||
}
|
||||
base_type = t.normalize_type_alias(base_type)
|
||||
if base_type.starts_with('&') {
|
||||
ptr_elem_type := t.normalize_type_alias(base_type[1..])
|
||||
if !ptr_elem_type.starts_with('[]') {
|
||||
return ptr_elem_type
|
||||
}
|
||||
base_type = ptr_elem_type
|
||||
}
|
||||
if node.value == 'range' {
|
||||
if base_type == 'string' {
|
||||
return 'string'
|
||||
}
|
||||
if base_type.starts_with('[]') {
|
||||
return base_type
|
||||
}
|
||||
}
|
||||
if base_type.starts_with('[]') {
|
||||
return t.normalize_type_alias(base_type[2..])
|
||||
}
|
||||
if base_type.starts_with('map[') {
|
||||
bracket_end := base_type.index(']') or { return '' }
|
||||
if bracket_end + 1 < base_type.len {
|
||||
return t.normalize_type_alias(base_type[bracket_end + 1..])
|
||||
}
|
||||
return ''
|
||||
}
|
||||
if base_type == 'string' {
|
||||
return 'u8'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// node_type returns the v-type string for an expression node, preferring the
|
||||
// precise type recorded by the type checker (tc.expr_types, populated before
|
||||
// transform) and falling back to the transformer's local heuristics. This is the
|
||||
// bridge that makes the transformer type-aware, so type-dependent lowering lives
|
||||
// here rather than in the backend.
|
||||
fn (t &Transformer) node_type(id flat.NodeId) string {
|
||||
if int(id) < 0 {
|
||||
return ''
|
||||
}
|
||||
resolved := t.resolve_expr_type(id)
|
||||
if resolved.len > 0 {
|
||||
return resolved
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.typ.len > 0 {
|
||||
return t.normalize_type_alias(node.typ)
|
||||
}
|
||||
if node.kind == .selector {
|
||||
sel_type := t.resolve_selector_type(node)
|
||||
if sel_type.len > 0 {
|
||||
return sel_type
|
||||
}
|
||||
}
|
||||
if node.kind == .index {
|
||||
elem_type := t.resolve_index_elem_type(node)
|
||||
if elem_type.len > 0 {
|
||||
return elem_type
|
||||
}
|
||||
}
|
||||
if node.kind == .infix {
|
||||
infix_type := t.infix_struct_operator_result_type(node)
|
||||
if infix_type.len > 0 {
|
||||
return infix_type
|
||||
}
|
||||
}
|
||||
if node.kind == .struct_init && node.value.len > 0 {
|
||||
if node.value in t.structs {
|
||||
return node.value
|
||||
}
|
||||
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
||||
qname := '${t.cur_module}.${node.value}'
|
||||
if qname in t.structs {
|
||||
return qname
|
||||
}
|
||||
}
|
||||
return node.value
|
||||
}
|
||||
if !isnil(t.tc) {
|
||||
if typ := t.tc.expr_type(id) {
|
||||
name := typ.name()
|
||||
if name.len > 0 && name != 'void' && (name != 'int'
|
||||
|| node.kind in [.ident, .int_literal, .infix, .prefix, .paren, .selector, .index, .call]) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// lvalue_type returns the v-type string for an assignable expression, handling
|
||||
// idents/calls (via node_type), selectors (struct field types) and index
|
||||
// expressions (array/map element types). Used by array-append lowering so that
|
||||
// `obj.field << x` and `m[k] << x` are typed, not just plain `arr << x`.
|
||||
fn (t &Transformer) lvalue_type(id flat.NodeId) string {
|
||||
if int(id) < 0 {
|
||||
return ''
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .selector {
|
||||
sel_type := t.resolve_selector_type(node)
|
||||
if !isnil(t.tc) {
|
||||
if checker_type := t.tc.expr_type(id) {
|
||||
checker_name := checker_type.name()
|
||||
if checker_name.starts_with('&') && !sel_type.starts_with('&') {
|
||||
return checker_name
|
||||
}
|
||||
}
|
||||
}
|
||||
if sel_type.len > 0 {
|
||||
return sel_type
|
||||
}
|
||||
}
|
||||
if node.kind == .index {
|
||||
elem_type := t.resolve_index_elem_type(node)
|
||||
if elem_type.len > 0 {
|
||||
return elem_type
|
||||
}
|
||||
}
|
||||
nt := t.node_type(id)
|
||||
if nt.len > 0 {
|
||||
return nt
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// is_string_type checks if an expression resolves to string type.
|
||||
// Handles string literals, interpolations, and ident/call expressions typed as string.
|
||||
fn (t &Transformer) is_string_type(id flat.NodeId) bool {
|
||||
if int(id) < 0 {
|
||||
return false
|
||||
}
|
||||
node := t.a.nodes[int(id)]
|
||||
if node.kind == .string_literal || node.kind == .string_interp {
|
||||
return true
|
||||
}
|
||||
return t.node_type(id) == 'string'
|
||||
}
|
||||
|
||||
// is_array_type checks if a type string represents an array type (starts with `[]`).
|
||||
fn (t &Transformer) is_array_type(type_str string) bool {
|
||||
return type_str.starts_with('[]')
|
||||
}
|
||||
|
||||
// is_map_type checks if a type string represents a map type (starts with `map[`).
|
||||
fn (t &Transformer) is_map_type(type_str string) bool {
|
||||
return type_str.starts_with('map[')
|
||||
}
|
||||
|
||||
// array_elem_type extracts the element type from an array type string.
|
||||
// `[]int` -> `int`, `[][]string` -> `[]string`.
|
||||
fn (t &Transformer) array_elem_type(type_str string) string {
|
||||
if type_str.starts_with('[]') {
|
||||
return type_str[2..]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// map_value_type extracts the value type from a map type string.
|
||||
// `map[string]int` -> `int`, `map[string][]bool` -> `[]bool`.
|
||||
fn (t &Transformer) map_value_type(type_str string) string {
|
||||
if !type_str.starts_with('map[') {
|
||||
return ''
|
||||
}
|
||||
bracket_end := type_str.index(']') or { return '' }
|
||||
if bracket_end + 1 < type_str.len {
|
||||
return type_str[bracket_end + 1..]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// map_key_type extracts the key type from a map type string.
|
||||
// `map[string]int` -> `string`, `map[int]bool` -> `int`.
|
||||
fn (t &Transformer) map_key_type(type_str string) string {
|
||||
if !type_str.starts_with('map[') {
|
||||
return ''
|
||||
}
|
||||
bracket_end := type_str.index(']') or { return '' }
|
||||
if bracket_end > 4 {
|
||||
return type_str[4..bracket_end]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
Reference in new issue
Block a user