Files

874 lines
32 KiB
V

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
})
}