1238 lines
34 KiB
V
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
|
|
}
|