Files
agila_open_tool/tools/v/vlib/v3/transform/expr.v
T

1238 lines
34 KiB
V

module transform
import v3.flat
fn (mut t Transformer) transform_infix_string_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.children_count < 2 {
return none
}
match node.op {
.plus, .eq, .ne, .lt, .gt, .le, .ge {}
else {
return none
}
}
lhs_id := t.a.children[node.children_start]
rhs_id := t.a.children[node.children_start + 1]
is_string := t.is_string_type(lhs_id) || t.is_string_type(rhs_id)
if !is_string {
return none
}
new_lhs := t.transform_expr(lhs_id)
new_rhs := t.transform_expr(rhs_id)
match node.op {
.plus {
lhs := t.a.nodes[int(new_lhs)]
rhs := t.a.nodes[int(new_rhs)]
if lhs.kind == .string_literal && rhs.kind == .string_literal {
return t.make_string_literal(lhs.value + rhs.value)
}
return t.make_call('string__plus', arr2(new_lhs, new_rhs))
}
.eq {
return t.make_call('string__eq', arr2(new_lhs, new_rhs))
}
.ne {
eq_call := t.make_call('string__eq', arr2(new_lhs, new_rhs))
start := t.a.children.len
t.a.children << eq_call
return t.a.add_node(flat.Node{
kind: .prefix
op: .not
children_start: start
children_count: 1
})
}
.lt {
return t.make_call('string__lt', arr2(new_lhs, new_rhs))
}
.gt {
// a > b -> string__lt(b, a)
return t.make_call('string__lt', arr2(new_rhs, new_lhs))
}
.le {
// a <= b -> !(b < a) -> !string__lt(rhs, lhs)
lt_call := t.make_call('string__lt', arr2(new_rhs, new_lhs))
start := t.a.children.len
t.a.children << lt_call
return t.a.add_node(flat.Node{
kind: .prefix
op: .not
children_start: start
children_count: 1
})
}
.ge {
// a >= b -> !(a < b) -> !string__lt(lhs, rhs)
lt_call := t.make_call('string__lt', arr2(new_lhs, new_rhs))
start := t.a.children.len
t.a.children << lt_call
return t.a.add_node(flat.Node{
kind: .prefix
op: .not
children_start: start
children_count: 1
})
}
else {
return none
}
}
}
fn (mut t Transformer) transform_infix_array_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.children_count < 2 || node.op !in [.eq, .ne] {
return none
}
lhs_id := t.a.children[node.children_start]
rhs_id := t.a.children[node.children_start + 1]
lhs_type := t.membership_container_type(t.node_type(lhs_id))
rhs_type := t.membership_container_type(t.node_type(rhs_id))
if !(lhs_type.starts_with('[]') || lhs_type == 'array') || !(rhs_type.starts_with('[]')
|| rhs_type == 'array') {
return none
}
mut elem_type := ''
if lhs_type.starts_with('[]') {
elem_type = lhs_type[2..]
} else if rhs_type.starts_with('[]') {
elem_type = rhs_type[2..]
}
if elem_type.len == 0 {
elem_type = 'int'
}
new_lhs := t.transform_expr(lhs_id)
new_rhs := t.transform_expr(rhs_id)
eq_call := if elem_type == 'string' {
t.make_call_typed('array_eq_string', arr2(new_lhs, new_rhs), 'bool')
} else {
t.make_call_typed('array_eq_raw', arr3(new_lhs, new_rhs, t.make_sizeof_type(elem_type)),
'bool')
}
if node.op == .ne {
return t.make_prefix(.not, eq_call)
}
return eq_call
}
fn (mut t Transformer) transform_infix_map_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.children_count < 2 || node.op !in [.eq, .ne] {
return none
}
lhs_id := t.a.children[node.children_start]
rhs_id := t.a.children[node.children_start + 1]
lhs_type := t.node_type(lhs_id)
rhs_type := t.node_type(rhs_id)
lhs_map_type := t.clean_map_type(lhs_type)
rhs_map_type := t.clean_map_type(rhs_type)
if !lhs_map_type.starts_with('map[') || !rhs_map_type.starts_with('map[') {
return none
}
mut new_lhs := t.transform_expr(lhs_id)
mut new_rhs := t.transform_expr(rhs_id)
if lhs_type.starts_with('&') {
new_lhs = t.make_prefix(.mul, new_lhs)
}
if rhs_type.starts_with('&') {
new_rhs = t.make_prefix(.mul, new_rhs)
}
eq_call := t.make_call_typed('map_map_eq', arr2(new_lhs, new_rhs), 'bool')
if node.op == .ne {
return t.make_prefix(.not, eq_call)
}
return eq_call
}
fn (mut t Transformer) transform_infix_struct_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.children_count < 2 {
return none
}
lhs_id := t.a.children[node.children_start]
mut lhs_type := t.node_type(lhs_id)
if lhs_type.starts_with('&') {
return none
}
struct_type := t.struct_lookup_name(lhs_type)
if struct_type.len == 0 {
return none
}
if call_info := t.struct_operator_call_info(struct_type, node.op) {
if t.is_disabled_fn_name(call_info.name) {
ret_type := t.struct_operator_return_type(call_info.name)
if ret_type.len == 0 || ret_type == 'void' {
return t.make_empty()
}
return t.zero_value_for_type(ret_type)
}
lhs := t.transform_expr(lhs_id)
rhs := t.transform_expr(t.a.children[node.children_start + 1])
mut call_lhs := lhs
mut call_rhs := rhs
if call_info.reverse {
call_lhs = t.stable_transformed_expr_for_reuse(lhs, lhs_type, 'op_lhs')
call_rhs = t.stable_transformed_expr_for_reuse(rhs, t.node_type(t.a.children[
node.children_start + 1]), 'op_rhs')
}
args := if call_info.reverse {
arr2(call_rhs, call_lhs)
} else {
arr2(call_lhs, call_rhs)
}
call := t.make_call_typed(call_info.name, args, node.typ)
if call_info.negate {
return t.make_prefix(.not, call)
}
return call
}
if node.op != .eq && node.op != .ne {
return none
}
if !t.has_struct_operator_fn(struct_type, '==') {
lhs := t.stable_expr_for_reuse(lhs_id)
rhs := t.stable_expr_for_reuse(t.a.children[node.children_start + 1])
cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
t.make_sizeof_type(struct_type)), 'int')
return t.make_infix(node.op, cmp, t.make_int_literal(0))
}
if eq_fn := t.struct_operator_fn_name(struct_type, '==') {
if t.is_disabled_fn_name(eq_fn) {
return t.make_bool_literal(node.op == .ne)
}
lhs := t.transform_expr(lhs_id)
rhs := t.transform_expr(t.a.children[node.children_start + 1])
eq_call := t.make_call_typed(eq_fn, arr2(lhs, rhs), 'bool')
if node.op == .ne {
return t.make_prefix(.not, eq_call)
}
return eq_call
}
return none
}
struct StructOperatorCallInfo {
name string
reverse bool
negate bool
}
fn (t &Transformer) struct_operator_call_info(struct_type string, op flat.Op) ?StructOperatorCallInfo {
if op_name := struct_operator_symbol(op) {
if method_name := t.struct_operator_fn_name(struct_type, op_name) {
return StructOperatorCallInfo{
name: method_name
}
}
}
match op {
.gt {
if method_name := t.struct_operator_fn_name(struct_type, '<') {
return StructOperatorCallInfo{
name: method_name
reverse: true
}
}
}
.ge {
if method_name := t.struct_operator_fn_name(struct_type, '<') {
return StructOperatorCallInfo{
name: method_name
negate: true
}
}
}
.le {
if method_name := t.struct_operator_fn_name(struct_type, '<') {
return StructOperatorCallInfo{
name: method_name
reverse: true
negate: true
}
}
}
.ne {
if method_name := t.struct_operator_fn_name(struct_type, '==') {
return StructOperatorCallInfo{
name: method_name
negate: true
}
}
}
else {}
}
return none
}
fn struct_operator_symbol(op flat.Op) ?string {
match op {
.plus { return '+' }
.minus { return '-' }
.mul { return '*' }
.div { return '/' }
.mod { return '%' }
.eq { return '==' }
.ne { return '!=' }
.lt { return '<' }
.gt { return '>' }
.le { return '<=' }
.ge { return '>=' }
else {}
}
return none
}
fn (t &Transformer) struct_operator_fn_name(struct_type string, op_name string) ?string {
method_name := '${struct_type}.${op_name}'
if t.is_known_fn_name(method_name) {
return method_name
}
cmethod_name := c_name(method_name)
if t.is_known_fn_name(cmethod_name) {
return cmethod_name
}
return none
}
fn (t &Transformer) has_struct_operator_fn(struct_type string, op_name string) bool {
if _ := t.struct_operator_fn_name(struct_type, op_name) {
return true
}
return false
}
fn (t &Transformer) struct_operator_return_type(fn_name string) string {
if ret := t.fn_ret_types[fn_name] {
return t.normalize_type_alias(ret)
}
if !isnil(t.tc) {
if ret := t.tc.fn_ret_types[fn_name] {
return t.normalize_type_alias(ret.name())
}
}
return ''
}
fn (t &Transformer) infix_struct_operator_result_type(node flat.Node) string {
if node.children_count < 2 {
return ''
}
lhs_id := t.a.child(&node, 0)
mut lhs_type := t.node_type(lhs_id)
if lhs_type.starts_with('&') {
lhs_type = lhs_type[1..]
}
struct_type := t.struct_lookup_name(lhs_type)
if struct_type.len == 0 {
return ''
}
if call_info := t.struct_operator_call_info(struct_type, node.op) {
ret_type := t.struct_operator_return_type(call_info.name)
if ret_type.len > 0 {
return ret_type
}
}
return ''
}
fn (mut t Transformer) transform_infix_sum_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.op !in [.eq, .ne] || node.children_count < 2 {
return none
}
lhs_id := t.a.children[node.children_start]
rhs_id := t.a.children[node.children_start + 1]
lhs_raw_type := t.node_type(lhs_id)
rhs_raw_type := t.node_type(rhs_id)
mut lhs_type := lhs_raw_type
mut rhs_type := rhs_raw_type
lhs_is_ptr := lhs_type.starts_with('&')
rhs_is_ptr := rhs_type.starts_with('&')
if lhs_is_ptr {
lhs_type = lhs_type[1..]
}
if rhs_is_ptr {
rhs_type = rhs_type[1..]
}
if !t.is_sum_type_name(lhs_type) || !t.is_sum_type_name(rhs_type) {
return none
}
sum_type := if t.is_sum_type_name(lhs_type) {
t.resolve_sum_name(lhs_type)
} else if t.is_sum_type_name(rhs_type) {
t.resolve_sum_name(rhs_type)
} else {
''
}
if sum_type.len == 0 {
return none
}
mut lhs := t.stable_expr_for_reuse(lhs_id)
mut rhs := t.stable_expr_for_reuse(rhs_id)
if lhs_is_ptr {
lhs = t.make_prefix(.mul, lhs)
t.a.nodes[int(lhs)].typ = lhs_type
}
if rhs_is_ptr {
rhs = t.make_prefix(.mul, rhs)
t.a.nodes[int(rhs)].typ = rhs_type
}
tag_eq := t.make_infix(.eq, t.make_selector(lhs, 'typ', 'int'), t.make_selector(rhs, 'typ',
'int'))
cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
t.make_sizeof_type(sum_type)), 'int')
value_eq := t.make_infix(.eq, cmp, t.make_int_literal(0))
eq := t.make_infix(.logical_and, tag_eq, value_eq)
if node.op == .ne {
return t.make_prefix(.not, t.make_paren(eq))
}
return eq
}
fn (mut t Transformer) transform_infix_optional_none_ops(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.op !in [.eq, .ne] || node.children_count < 2 {
return none
}
lhs_id := t.a.children[node.children_start]
rhs_id := t.a.children[node.children_start + 1]
lhs := t.a.nodes[int(lhs_id)]
rhs := t.a.nodes[int(rhs_id)]
mut opt_id := flat.empty_node
if lhs.kind == .none_expr {
opt_id = rhs_id
} else if rhs.kind == .none_expr {
opt_id = lhs_id
} else {
return none
}
opt_type := t.node_type(opt_id)
if !t.is_optional_type_name(opt_type) {
return none
}
ok := t.make_selector(t.transform_expr(opt_id), 'ok', 'bool')
if node.op == .eq {
return t.make_prefix(.not, ok)
}
return ok
}
fn (t &Transformer) struct_lookup_name(type_name string) string {
if type_name.len == 0 {
return ''
}
if type_name.contains('.') {
if type_name in t.structs {
return type_name
}
short_type := type_name.all_after_last('.')
type_mod := type_name.all_before_last('.')
if info := t.structs[short_type] {
if info.module == type_mod {
return short_type
}
}
if info := t.structs[type_name] {
if info.module == type_mod {
return type_name
}
}
return ''
}
if type_name in t.structs {
if info := t.structs[type_name] {
if info.module.len == 0 || info.module == t.cur_module {
return type_name
}
}
}
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
qtype := '${t.cur_module}.${type_name}'
if qtype in t.structs {
return qtype
}
}
return ''
}
fn (mut t Transformer) transform_in_expr(id flat.NodeId, node flat.Node) flat.NodeId {
if node.children_count < 2 {
return id
}
lhs_id := t.a.children[node.children_start]
rhs_id := t.a.children[node.children_start + 1]
rhs := t.a.nodes[int(rhs_id)]
is_not_in := node.value == '!in'
mut result := id
if rhs.kind == .range {
// x in low..high -> x >= low && x < high
if rhs.children_count >= 2 {
new_lhs := t.stable_expr_for_reuse(lhs_id)
low_id := t.a.children[rhs.children_start]
high_id := t.a.children[rhs.children_start + 1]
new_low := t.transform_expr(low_id)
new_high := t.transform_expr(high_id)
ge_cmp := t.make_infix(.ge, new_lhs, new_low)
lt_cmp := t.make_infix(.lt, new_lhs, new_high)
result = t.make_infix(.logical_and, ge_cmp, lt_cmp)
}
} else if rhs.kind == .array_literal {
if type_checks := t.lower_type_pattern_membership(lhs_id, rhs, is_not_in) {
return type_checks
}
// x in [a, b, c] -> x == a || x == b || x == c
if rhs.children_count == 0 {
result = t.make_bool_literal(false)
} else {
new_lhs := t.stable_expr_for_reuse(lhs_id)
is_str := t.is_string_type(lhs_id)
mut or_chain := flat.empty_node
for i in 0 .. rhs.children_count {
elem_id := t.a.children[rhs.children_start + i]
new_elem := t.transform_expr(elem_id)
eq_cmp := if is_str {
t.make_call('string__eq', arr2(new_lhs, new_elem))
} else {
t.make_infix(.eq, new_lhs, new_elem)
}
if int(or_chain) < 0 {
or_chain = eq_cmp
} else {
or_chain = t.make_infix(.logical_or, or_chain, eq_cmp)
}
}
result = or_chain
}
} else {
rhs_type := t.node_type(rhs_id)
clean_rhs_type := t.membership_container_type(rhs_type)
rhs_is_ptr_array := t.membership_container_is_pointer_array(rhs_type)
if clean_rhs_type.starts_with('[]') || clean_rhs_type == 'array' {
if lowered := t.lower_array_membership_expr(rhs_id, lhs_id, rhs_type, false, node) {
result = lowered
} else {
// dynamic array membership -> array_contains_int/string(arr, val)
new_lhs := t.transform_expr(lhs_id)
mut new_rhs := t.transform_expr(rhs_id)
if rhs_is_ptr_array {
new_rhs = t.make_prefix(.mul, new_rhs)
}
mut elem := if clean_rhs_type.starts_with('[]') { clean_rhs_type[2..] } else { '' }
if elem.len == 0 {
elem = t.node_type(lhs_id)
}
fn_name := array_contains_fn_name(elem)
result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
}
} else if rhs.kind in [.ident, .selector] && (rhs_type.len == 0 || rhs_type == 'unknown') {
new_lhs := t.transform_expr(lhs_id)
new_rhs := t.transform_expr(rhs_id)
mut elem := t.node_type(lhs_id)
lhs := t.a.nodes[int(lhs_id)]
if elem.len == 0 && lhs.kind == .selector {
elem = t.selector_field_type(lhs)
}
if elem.len == 0 && rhs.kind == .selector {
elem = t.selector_array_elem_type(rhs)
}
if elem.len > 0 {
fn_name := array_contains_fn_name(elem)
result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
}
} else if is_fixed_array_type(clean_rhs_type) {
// fixed array membership -> fixed_array_contains_int/string(arr, len, val)
new_lhs := t.transform_expr(lhs_id)
new_rhs := t.transform_expr(rhs_id)
elem := fixed_array_elem_type(clean_rhs_type)
fn_name := fixed_array_contains_fn_name(elem)
len_expr := t.make_fixed_array_len_expr(clean_rhs_type)
result = t.make_call_typed(fn_name, arr3(new_rhs, len_expr, new_lhs), 'bool')
} else if clean_rhs_type == 'string' {
new_lhs := t.transform_expr(lhs_id)
new_rhs := t.transform_expr(rhs_id)
fn_name := if t.node_type(lhs_id) in ['u8', 'byte'] {
'string__contains_u8'
} else {
'string__contains'
}
result = t.make_call_typed(fn_name, arr2(new_rhs, new_lhs), 'bool')
} else if clean_rhs_type.starts_with('map[') || clean_rhs_type == 'map' {
if lowered := t.lower_map_membership_expr(rhs_id, lhs_id, rhs_type) {
result = lowered
}
} else {
// Unknown containment is kept as in_expr so the backend can reject or
// handle genuinely unresolved cases.
new_lhs := t.transform_expr(lhs_id)
new_rhs := t.transform_expr(rhs_id)
in_start := t.a.children.len
t.a.children << new_lhs
t.a.children << new_rhs
result = t.a.add_node(flat.Node{
kind: .in_expr
op: node.op
children_start: in_start
children_count: 2
pos: node.pos
value: 'in'
typ: node.typ
})
}
}
if is_not_in && result != id {
start := t.a.children.len
t.a.children << t.make_paren(result)
return t.a.add_node(flat.Node{
kind: .prefix
op: .not
children_start: start
children_count: 1
})
}
return result
}
fn (mut t Transformer) lower_type_pattern_membership(lhs_id flat.NodeId, rhs flat.Node, is_not_in bool) ?flat.NodeId {
if rhs.children_count == 0 {
return none
}
mut patterns := []string{cap: int(rhs.children_count)}
mut sum_name := t.trim_pointer_type(t.original_expr_type(lhs_id))
if !t.is_sum_type_name(sum_name) {
sum_name = t.trim_pointer_type(t.node_type(lhs_id))
}
for i in 0 .. rhs.children_count {
elem_id := t.a.child(&rhs, i)
pattern := t.type_pattern_name(elem_id)
if pattern.len == 0 {
return none
}
if !t.is_sum_type_name(sum_name) {
pattern_sum := t.find_sum_type_for_variant(pattern)
if pattern_sum.len == 0 {
return none
}
sum_name = pattern_sum
} else if t.sum_variant_path(sum_name, pattern).len == 0 {
return none
}
patterns << pattern
}
if !t.is_sum_type_name(sum_name) {
return none
}
lhs_type := t.original_expr_type(lhs_id)
base := t.stable_expr_for_reuse(lhs_id)
mut chain := flat.empty_node
for pattern in patterns {
cmp := t.make_sum_type_pattern_check(base, lhs_type, sum_name, pattern) or { return none }
if int(chain) < 0 {
chain = cmp
} else {
chain = t.make_infix(.logical_or, chain, cmp)
}
}
if is_not_in {
return t.make_prefix(.not, t.make_paren(chain))
}
return chain
}
fn (t &Transformer) type_pattern_name(id flat.NodeId) string {
if int(id) < 0 {
return ''
}
node := t.a.nodes[int(id)]
match node.kind {
.ident {
return node.value
}
.selector {
if node.children_count == 0 {
return node.value
}
base := t.type_pattern_name(t.a.child(&node, 0))
if base.len == 0 {
return node.value
}
return '${base}.${node.value}'
}
.array_init {
if node.value.len == 0 {
return ''
}
return '[]${node.value}'
}
.array_literal {
if node.value.len == 0 {
return ''
}
return '[${node.children_count}]${node.value}'
}
else {
return ''
}
}
}
fn (mut t Transformer) lower_array_membership_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
clean_base_type := t.membership_container_type(base_type)
if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
return none
}
mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
if elem_type.len == 0 {
elem_type = t.membership_container_type(t.node_type(needle_id))
}
if elem_type.len == 0 {
return none
}
mut base := flat.empty_node
mut needle := flat.empty_node
if receiver_first {
base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
needle = t.stable_expr_for_reuse(needle_id)
} else {
needle = t.stable_expr_for_reuse(needle_id)
base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
}
mut prefix := []flat.NodeId{}
t.drain_pending(mut prefix)
result_name := t.new_temp('contains')
idx_name := t.new_temp('contains_idx')
prefix << t.make_decl_assign_typed(result_name, t.make_bool_literal(false), '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_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
assign_true := t.make_assign(t.make_ident(result_name), t.make_bool_literal(true))
then_block := t.make_block(arr1(assign_true))
loop_body := arr1(t.make_if(eq_expr, then_block, t.make_empty()))
prefix << t.make_for_stmt(init, cond, post, loop_body, src)
for stmt in prefix {
t.pending_stmts << stmt
}
return t.make_ident(result_name)
}
fn (mut t Transformer) lower_array_index_expr(base_id flat.NodeId, needle_id flat.NodeId, base_type string, receiver_first bool, src flat.Node) ?flat.NodeId {
clean_base_type := t.membership_container_type(base_type)
if !clean_base_type.starts_with('[]') && clean_base_type != 'array' {
return none
}
mut elem_type := if clean_base_type.starts_with('[]') { clean_base_type[2..] } else { '' }
if elem_type.len == 0 {
elem_type = t.membership_container_type(t.node_type(needle_id))
}
if elem_type.len == 0 {
return none
}
mut base := flat.empty_node
mut needle := flat.empty_node
if receiver_first {
base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
needle = t.stable_expr_for_reuse(needle_id)
} else {
needle = t.stable_expr_for_reuse(needle_id)
base = t.stable_array_expr_for_membership(base_id, base_type, clean_base_type)
}
mut prefix := []flat.NodeId{}
t.drain_pending(mut prefix)
result_name := t.new_temp('index')
idx_name := t.new_temp('index_idx')
prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(-1), '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_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
eq_expr := t.make_membership_eq_expr(elem_expr, needle, elem_type)
not_found := t.make_infix(.lt, t.make_ident(result_name), t.make_int_literal(0))
found_cond := t.make_infix(.logical_and, not_found, eq_expr)
assign_idx := t.make_assign(t.make_ident(result_name), t.make_ident(idx_name))
loop_body := arr1(t.make_if(found_cond, t.make_block(arr1(assign_idx)), t.make_empty()))
prefix << t.make_for_stmt(init, cond, post, loop_body, src)
for stmt in prefix {
t.pending_stmts << stmt
}
return t.make_ident(result_name)
}
fn (mut t Transformer) stable_array_expr_for_membership(id flat.NodeId, raw_type string, clean_type string) flat.NodeId {
mut expr := t.transform_expr(id)
if t.membership_container_is_pointer_array(raw_type) {
expr = t.make_prefix(.mul, expr)
}
return t.stable_transformed_expr_for_reuse(expr, clean_type, 'in_arr')
}
fn (mut t Transformer) make_membership_eq_expr(lhs flat.NodeId, rhs flat.NodeId, elem_type string) flat.NodeId {
mut clean := t.membership_container_type(elem_type)
if clean == 'string' {
return t.make_call_typed('string__eq', arr2(lhs, rhs), 'bool')
}
if clean.starts_with('[]') {
inner := clean[2..]
if inner == 'string' {
return t.make_call_typed('array_eq_string', arr2(lhs, rhs), 'bool')
}
return t.make_call_typed('array_eq_raw', arr3(lhs, rhs, t.make_sizeof_type(inner)), 'bool')
}
struct_type := t.struct_lookup_name(clean)
if struct_type.len > 0 {
method_name := '${struct_type}.=='
if t.is_known_fn_name(method_name) {
return t.make_call(method_name, arr2(lhs, rhs))
}
cmp := t.make_call_typed('memcmp', arr3(t.make_prefix(.amp, lhs), t.make_prefix(.amp, rhs),
t.make_sizeof_type(struct_type)), 'int')
return t.make_infix(.eq, cmp, t.make_int_literal(0))
}
return t.make_infix(.eq, lhs, rhs)
}
fn (t &Transformer) selector_field_type(node flat.Node) string {
if node.value.len == 0 {
return ''
}
for _, info in t.structs {
for field in info.fields {
if field.name == node.value {
return t.normalize_type_alias(field.typ)
}
}
}
return ''
}
fn (t &Transformer) selector_array_elem_type(node flat.Node) string {
if node.value.len == 0 {
return ''
}
for _, info in t.structs {
for field in info.fields {
clean_typ := t.membership_container_type(field.typ)
if field.name == node.value && clean_typ.starts_with('[]') {
return t.normalize_type_alias(clean_typ[2..])
}
}
}
return ''
}
fn (t &Transformer) membership_container_type(typ string) string {
mut clean := t.normalize_type_alias(typ).trim_space()
for {
if clean.starts_with('shared ') {
clean = clean[7..].trim_space()
continue
}
if clean.starts_with('&') {
clean = clean[1..].trim_space()
continue
}
if clean.starts_with('...') {
clean = '[]' + clean[3..].trim_space()
continue
}
break
}
return t.normalize_type_alias(clean)
}
fn (t &Transformer) membership_container_is_pointer_array(typ string) bool {
mut clean := t.normalize_type_alias(typ).trim_space()
for clean.starts_with('shared ') {
clean = clean[7..].trim_space()
}
if !clean.starts_with('&') {
return false
}
clean = t.membership_container_type(clean)
return clean.starts_with('[]') || clean == 'array'
}
fn array_contains_fn_name(elem string) string {
return match elem {
'string' { 'array_contains_string' }
'u8', 'byte' { 'array_contains_u8' }
else { 'array_contains_int' }
}
}
fn fixed_array_contains_fn_name(elem string) string {
return match elem {
'string' { 'fixed_array_contains_string' }
'u8', 'byte' { 'fixed_array_contains_u8' }
else { 'fixed_array_contains_int' }
}
}
fn (mut t Transformer) stable_expr_for_reuse(id flat.NodeId) flat.NodeId {
expr := t.transform_expr(id)
if t.is_stable_expr_for_reuse(expr) {
return expr
}
tmp_name := t.new_temp('in_lhs')
tmp_typ := t.node_type(id)
decl := t.make_decl_assign(tmp_name, expr)
if tmp_typ.len > 0 {
t.a.nodes[int(decl)].typ = tmp_typ
t.set_var_type(tmp_name, tmp_typ)
}
t.pending_stmts << decl
return t.make_ident(tmp_name)
}
fn (mut t Transformer) stable_transformed_expr_for_reuse(expr flat.NodeId, typ string, prefix string) flat.NodeId {
if t.is_stable_expr_for_reuse(expr) {
return expr
}
tmp_name := t.new_temp(prefix)
t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, typ)
return t.make_ident(tmp_name)
}
fn (t &Transformer) is_stable_expr_for_reuse(id flat.NodeId) bool {
if int(id) < 0 {
return true
}
node := t.a.nodes[int(id)]
return match node.kind {
.ident, .int_literal, .float_literal, .bool_literal, .char_literal, .string_literal,
.nil_literal, .none_expr, .enum_val, .sizeof_expr, .typeof_expr {
true
}
.selector {
node.children_count > 0 && t.is_stable_expr_for_reuse(t.a.children[node.children_start])
}
.index {
node.children_count >= 2
&& t.is_stable_expr_for_reuse(t.a.children[node.children_start])
&& t.is_stable_expr_for_reuse(t.a.children[node.children_start + 1])
}
else {
false
}
}
}
fn (mut t Transformer) transform_fixed_array_len(_id flat.NodeId, node flat.Node) ?flat.NodeId {
if node.value != 'len' || node.children_count == 0 {
return none
}
base_id := t.a.children[node.children_start]
base_type := t.node_type(base_id)
if !is_fixed_array_type(base_type) {
return none
}
return t.make_fixed_array_len_expr(base_type)
}
fn (t &Transformer) clean_map_type(typ string) string {
mut clean := t.normalize_type_alias(typ).trim_space()
for {
if clean.starts_with('shared ') {
clean = clean[7..].trim_space()
continue
}
if clean.starts_with('&') {
clean = clean[1..].trim_space()
continue
}
break
}
return t.normalize_type_alias(clean)
}
fn (mut t Transformer) runtime_addr(expr flat.NodeId, typ string) flat.NodeId {
if typ.starts_with('&') {
return expr
}
if !t.expr_can_take_address(expr) {
stable := t.stable_transformed_expr_for_reuse(expr, typ, 'addr')
return t.make_prefix(.amp, stable)
}
return t.make_prefix(.amp, expr)
}
fn (mut t Transformer) transform_enum_shorthand(id flat.NodeId, node flat.Node, expected_enum string) flat.NodeId {
resolved_enum := t.enum_type_name_for_expected(expected_enum, '')
if resolved_enum.len == 0 {
return id
}
short_name := node.value.trim_left('.')
if fields := t.enum_types[resolved_enum] {
for f in fields {
if f == short_name {
return t.a.add_node(flat.Node{
kind: .enum_val
value: '${resolved_enum}.${short_name}'
typ: resolved_enum
})
}
}
}
return id
}
fn (t &Transformer) enum_type_name_for_expected(expected_enum string, owner_mod string) string {
if expected_enum.len == 0 {
return ''
}
mut clean := expected_enum
if clean.starts_with('&') {
clean = clean[1..]
}
if clean in t.enum_types {
return clean
}
if clean.contains('.') {
return ''
}
for mod_name in [owner_mod, t.cur_module] {
if mod_name.len == 0 || mod_name == 'main' || mod_name == 'builtin' {
continue
}
qname := '${mod_name}.${clean}'
if qname in t.enum_types {
return qname
}
}
mut found := ''
for enum_name, _ in t.enum_types {
if enum_name.all_after_last('.') != clean {
continue
}
if found.len > 0 && found != enum_name {
return ''
}
found = enum_name
}
return found
}
pub fn (mut t Transformer) make_call(fn_name string, args []flat.NodeId) flat.NodeId {
return t.make_call_typed(fn_name, args, '')
}
pub fn (mut t Transformer) make_call_typed(fn_name string, args []flat.NodeId, typ string) flat.NodeId {
fn_ident := t.make_ident(fn_name)
return t.make_call_expr_typed(fn_ident, args, typ)
}
pub fn (mut t Transformer) make_call_expr_typed(fn_expr flat.NodeId, args []flat.NodeId, typ string) flat.NodeId {
start := t.a.children.len
t.a.children << fn_expr
for arg in args {
t.a.children << arg
}
return t.a.add_node(flat.Node{
kind: .call
children_start: start
children_count: flat.child_count(1 + args.len)
typ: typ
})
}
pub fn (mut t Transformer) make_empty() flat.NodeId {
return t.a.add(.empty)
}
pub fn (mut t Transformer) make_method_call(receiver flat.NodeId, method_name string, args []flat.NodeId) flat.NodeId {
// Build selector: receiver.method_name
sel_start := t.a.children.len
t.a.children << receiver
selector := t.a.add_node(flat.Node{
kind: .selector
value: method_name
children_start: sel_start
children_count: 1
})
start := t.a.children.len
t.a.children << selector
for arg in args {
t.a.children << arg
}
return t.a.add_node(flat.Node{
kind: .call
children_start: start
children_count: flat.child_count(1 + args.len)
})
}
pub fn (mut t Transformer) make_selector(base flat.NodeId, field string, typ string) flat.NodeId {
return t.make_selector_op(base, field, typ, .dot)
}
pub fn (mut t Transformer) make_selector_op(base flat.NodeId, field string, typ string, op flat.Op) flat.NodeId {
start := t.a.children.len
t.a.children << base
return t.a.add_node(flat.Node{
kind: .selector
op: op
children_start: start
children_count: 1
value: field
typ: typ
})
}
pub fn (mut t Transformer) make_index(base flat.NodeId, index flat.NodeId, typ string) flat.NodeId {
start := t.a.children.len
t.a.children << base
t.a.children << index
return t.a.add_node(flat.Node{
kind: .index
children_start: start
children_count: 2
typ: typ
})
}
pub fn (mut t Transformer) make_cast(target_type string, expr flat.NodeId, typ string) flat.NodeId {
start := t.a.children.len
t.a.children << expr
return t.a.add_node(flat.Node{
kind: .cast_expr
children_start: start
children_count: 1
value: target_type
typ: typ
})
}
pub fn (mut t Transformer) make_postfix(expr flat.NodeId, op flat.Op) flat.NodeId {
start := t.a.children.len
t.a.children << expr
return t.a.add_node(flat.Node{
kind: .postfix
op: op
children_start: start
children_count: 1
})
}
pub fn (mut t Transformer) make_struct_init(name string) flat.NodeId {
return t.a.add_node(flat.Node{
kind: .struct_init
value: name
typ: name
})
}
pub fn (mut t Transformer) make_array_init(elem_type string) flat.NodeId {
return t.a.add_node(flat.Node{
kind: .array_init
value: elem_type
typ: '[]${elem_type}'
})
}
pub fn (mut t Transformer) make_map_init(map_type string) flat.NodeId {
return t.a.add_node(flat.Node{
kind: .map_init
value: map_type
typ: map_type
})
}
pub fn (mut t Transformer) make_string_literal(value string) flat.NodeId {
return t.a.add_val(.string_literal, value)
}
pub fn (mut t Transformer) make_int_literal(value int) flat.NodeId {
return t.a.add_val(.int_literal, '${value}')
}
pub fn (mut t Transformer) make_float_literal(value string) flat.NodeId {
return t.a.add_val(.float_literal, value)
}
pub fn (mut t Transformer) make_bool_literal(value bool) flat.NodeId {
return t.a.add_val(.bool_literal, if value { 'true' } else { 'false' })
}
pub fn (mut t Transformer) make_sizeof_type(type_name string) flat.NodeId {
return t.a.add_node(flat.Node{
kind: .sizeof_expr
value: type_name
typ: 'usize'
})
}
// is_fixed_array_type reports whether a v-type string denotes a fixed array
// like `int[5]` (as opposed to a dynamic array `[]int` or a map `map[...]...`).
fn is_fixed_array_type(s string) bool {
if s.starts_with('[]') || s.starts_with('map[') {
return false
}
return s.contains('[') && (s.ends_with(']') || s.starts_with('['))
}
fn fixed_array_len(s string) int {
return fixed_array_len_text(s).int()
}
fn fixed_array_len_text(s string) string {
return s.all_after('[').all_before(']').trim_space()
}
fn fixed_array_elem_type(s string) string {
if s.starts_with('[') {
return s.all_after(']')
}
return s.all_before('[')
}
fn is_decimal_text(s string) bool {
if s.len == 0 {
return false
}
for ch in s {
if ch < `0` || ch > `9` {
return false
}
}
return true
}
fn (mut t Transformer) make_fixed_array_len_expr(s string) flat.NodeId {
len_text := fixed_array_len_text(s)
if is_decimal_text(len_text) {
return t.make_int_literal(len_text.int())
}
if len_text.contains('.') {
base := len_text.all_before_last('.')
field := len_text.all_after_last('.')
return t.make_selector(t.make_ident(base), field, 'int')
}
return t.make_ident(len_text)
}
const c_reserved_words = ['auto', 'break', 'case', 'char', 'const', 'continue', 'copy', 'default',
'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']
fn c_name(name string) string {
if name.starts_with('C.') {
return name[2..]
}
n := name.replace('[]', 'Array_').replace('.-', '__minus').replace('.+', '__plus').replace('.==',
'__eq').replace('.!=', '__ne').replace('.<=', '__le').replace('.>=', '__ge').replace('.<',
'__lt').replace('.>', '__gt').replace('&', 'ptr').replace('[', '_').replace(']', '').replace(',', '_').replace(' ', '_').replace('.', '__')
if n in c_reserved_words {
return 'v_${n}'
}
return n
}