Files
agila_open_tool/tools/v/vlib/v3/gen/c/fn.v
T

1891 lines
52 KiB
V

module c
import v3.flat
import v3.types
fn (mut g FlatGen) gen_fns() {
mut cur_module := ''
for i in 0 .. g.a.nodes.len {
node := g.a.nodes[i]
kind_id := node_kind_id(node)
if kind_id == 77 {
cur_module = ''
g.tc.cur_module = cur_module
continue
}
if kind_id == 73 {
cur_module = node.value
g.tc.cur_module = cur_module
continue
}
if kind_id == 61 {
if !g.should_emit_fn_node_in_module(node, i, cur_module) {
continue
}
qfn := qualified_fn_name_in_module(cur_module, node.value)
if g.emitted_fn_contains(qfn) {
continue
}
g.emitted_fns[qfn] = true
g.tc.cur_module = cur_module
g.gen_fn_in_module(node, cur_module)
}
}
}
fn (mut g FlatGen) should_emit_fn_node(node flat.Node, node_index int) bool {
return g.should_emit_fn_node_in_module(node, node_index, g.tc.cur_module)
}
fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int, module_name string) bool {
_ = node_index
dfn := dotted_fn_name_in_module(module_name, node.value)
cfn := c_name(node.value)
qfn := qualified_fn_name_in_module(module_name, node.value)
if cfn in ['Array_string__join', 'array_string_join'] {
return false
}
if module_name == 'main' {
return !g.has_generic_params(node)
}
if g.has_used_fn_filter() && !g.used_fn_contains(node.value) && !g.used_fn_contains(dfn)
&& !g.used_fn_contains(cfn) && !g.used_fn_contains(qfn) {
return false
}
if g.has_generic_params(node) {
return false
}
return true
}
fn (g &FlatGen) used_fn_contains(name string) bool {
if name.len == 0 {
return false
}
return g.used_fns[name]
}
fn (g &FlatGen) has_used_fn_filter() bool {
return g.used_fns.len > 0 && g.used_fn_contains('main')
}
fn (g &FlatGen) emitted_fn_contains(name string) bool {
return name.len > 0 && g.emitted_fns[name]
}
fn (g &FlatGen) qualified_fn_name(name string) string {
return qualified_fn_name_in_module(g.tc.cur_module, name)
}
fn qualified_fn_name_in_module(module_name string, name string) string {
if module_name == 'builtin' && name == 'free' {
return 'v_free'
}
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
return c_name('${module_name}.${name}')
}
if name == 'free' {
return 'v_free'
}
return c_name(name)
}
fn (g &FlatGen) direct_call_name(name string) string {
if name == 'free' {
return 'v_free'
}
if name == 'int_str' {
return 'int__str'
}
if name == 'bool_str' {
return 'bool__str'
}
return c_name(name)
}
fn (g &FlatGen) dotted_fn_name(name string) string {
return dotted_fn_name_in_module(g.tc.cur_module, name)
}
fn dotted_fn_name_in_module(module_name string, name string) string {
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
return '${module_name}.${name}'
}
return name
}
fn qualify_name_in_module(module_name string, name string) string {
if module_name.len == 0 || module_name == 'main' || module_name == 'builtin' {
return name
}
if name.contains('.') {
return name
}
return '${module_name}.${name}'
}
fn (mut g FlatGen) gen_fn(node flat.Node) {
g.gen_fn_in_module(node, g.tc.cur_module)
}
fn (mut g FlatGen) gen_fn_in_module(node flat.Node, module_name string) {
g.tc.cur_module = module_name
g.cur_fn_name = node.value
g.tc.push_scope()
g.defers = []flat.NodeId{}
g.fn_defers = []flat.NodeId{}
g.fn_defer_counts = map[int]string{}
g.defer_capture_names = []string{}
g.defer_capture_types = map[string]types.Type{}
g.set_cur_fn_ret(types.Type(types.void_))
old_param_names := g.cur_param_names.clone()
old_param_type_values := g.cur_param_type_values.clone()
old_param_types := g.cur_param_types.clone()
g.cur_param_names = []string{}
g.cur_param_type_values = []types.Type{}
g.cur_param_types = map[string]types.Type{}
for i in 0 .. node.children_count {
param_id := g.a.child(&node, i)
p := g.a.node(param_id)
if node_kind_id(p) == 75 && p.value.len > 0 {
param_type := g.tc.parse_type(p.typ)
g.cur_param_names << p.value
g.cur_param_type_values << param_type
g.cur_param_types[p.value] = param_type
g.tc.cur_scope.insert(p.value, param_type)
}
}
fn_defer_ids := g.collect_function_defer_ids(node)
g.prepare_function_defers(fn_defer_ids)
is_entry_main := node.value == 'main' && (module_name.len == 0 || module_name == 'main')
if is_entry_main {
g.writeln('int main(int argc, char** argv) {')
if g.has_builtins {
g.writeln('\tg_main_argc = argc;')
g.writeln('\tg_main_argv = argv;')
}
g.gen_compiler_vexe_env_setup()
if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 {
g.writeln('\t_vinit();')
}
} else {
ret_type := g.tc.parse_type(node.typ)
g.set_cur_fn_ret(ret_type)
g.write(g.optional_type_name(ret_type))
g.write(' ')
g.write(qualified_fn_name_in_module(module_name, node.value))
g.write('(')
g.write_fn_node_params(node)
g.writeln(') {')
}
g.indent++
g.gen_function_defer_prelude()
for i in 0 .. node.children_count {
id := g.a.child(&node, i)
child := g.a.node(id)
if child.kind != .param {
g.gen_node(id)
}
}
g.gen_all_defers()
if is_entry_main {
g.writeln('return 0;')
} else if g.cur_fn_ret_is_optional {
ct := g.optional_type_name(g.cur_fn_ret)
g.writeln('return (${ct}){.ok = true};')
}
g.indent--
g.writeln('}')
g.writeln('')
g.cur_param_names = old_param_names.clone()
g.cur_param_type_values = old_param_type_values.clone()
g.cur_param_types = old_param_types.clone()
g.tc.pop_scope()
}
fn (mut g FlatGen) collect_function_defer_ids(node flat.Node) []flat.NodeId {
mut ids := []flat.NodeId{}
for i in 0 .. node.children_count {
g.collect_function_defer_ids_from(g.a.child(&node, i), mut ids)
}
return ids
}
fn (mut g FlatGen) collect_function_defer_ids_from(id flat.NodeId, mut ids []flat.NodeId) {
if !g.valid_node_id(id) {
return
}
node := g.a.nodes[int(id)]
if node.kind == .fn_decl || node.kind == .c_fn_decl || node.kind == .fn_literal {
return
}
if node.kind == .defer_stmt && node.value == 'function' {
ids << id
return
}
for i in 0 .. node.children_count {
g.collect_function_defer_ids_from(g.a.child(&node, i), mut ids)
}
}
fn (mut g FlatGen) prepare_function_defers(fn_defer_ids []flat.NodeId) {
for idx, defer_id in fn_defer_ids {
g.fn_defer_counts[int(defer_id)] = '${c_name(g.cur_fn_name)}_defer_${idx}_count'
defer_node := g.a.nodes[int(defer_id)]
if defer_node.children_count > 0 {
g.collect_function_defer_captures(g.a.child(&defer_node, 0))
}
}
}
fn (mut g FlatGen) collect_function_defer_captures(id flat.NodeId) {
if !g.valid_node_id(id) {
return
}
node := g.a.nodes[int(id)]
if node.kind == .fn_decl || node.kind == .c_fn_decl || node.kind == .fn_literal {
return
}
if node.kind == .ident {
g.add_function_defer_capture(id, node.value)
}
for i in 0 .. node.children_count {
g.collect_function_defer_captures(g.a.child(&node, i))
}
}
fn (mut g FlatGen) add_function_defer_capture(id flat.NodeId, name string) {
if name.len == 0 || name == '_' || name in g.cur_param_names || name in g.modules
|| name in g.global_modules || name in g.defer_capture_types {
return
}
typ := g.usable_expr_type(id)
if typ is types.Void || typ is types.Unknown || typ is types.FnType {
return
}
ct := g.tc.c_type(typ)
if ct.len == 0 || ct == 'void' || ct.starts_with('fn_ptr:') {
return
}
g.defer_capture_names << name
g.defer_capture_types[name] = typ
}
fn (mut g FlatGen) gen_function_defer_prelude() {
for _, count_name in g.fn_defer_counts {
g.writeln('int ${count_name} = 0;')
}
for name in g.defer_capture_names {
typ := g.defer_capture_types[name] or { continue }
ct := g.tc.c_type(typ)
g.write('${ct} ${c_name(name)} = ')
g.gen_default_value_for_type(typ)
g.writeln(';')
g.tc.cur_scope.insert(name, typ)
}
}
fn (mut g FlatGen) set_cur_fn_ret(ret_type types.Type) {
g.cur_fn_ret = ret_type
g.cur_fn_ret_is_optional = false
g.cur_fn_ret_base = types.Type(types.void_)
if ret_type is types.OptionType {
g.cur_fn_ret_is_optional = true
g.cur_fn_ret_base = ret_type.base_type
} else if ret_type is types.ResultType {
g.cur_fn_ret_is_optional = true
g.cur_fn_ret_base = ret_type.base_type
}
}
fn (mut g FlatGen) gen_compiler_vexe_env_setup() {
if g.compiler_vroot.len == 0 {
return
}
root := c_escape(g.compiler_vroot)
g.writeln('\tif (getenv("VEXE") == NULL || getenv("VEXE")[0] == 0) {')
g.writeln('\t\tconst char* v3_arg0 = argc > 0 ? argv[0] : "v";')
g.writeln("\t\tconst char* v3_base = strrchr(v3_arg0, '/');")
g.writeln('\t\tv3_base = v3_base == NULL ? v3_arg0 : v3_base + 1;')
g.writeln('\t\tif (v3_base[0] == 0) v3_base = "v";')
g.writeln('\t\tchar v3_vexe_target[4096];')
g.writeln('\t\tsnprintf(v3_vexe_target, sizeof(v3_vexe_target), "${root}/%s", v3_base);')
g.writeln('\t\tchar v3_src_real[4096];')
g.writeln('\t\tchar v3_dst_real[4096];')
g.writeln('\t\tchar* v3_src_real_result = realpath(v3_arg0, v3_src_real);')
g.writeln('\t\tconst char* v3_src = v3_src_real_result != NULL ? v3_src_real : v3_arg0;')
g.writeln('\t\tint v3_same_exe = 0;')
g.writeln('\t\tif (v3_src_real_result != NULL && realpath(v3_vexe_target, v3_dst_real) != NULL && strcmp(v3_src_real, v3_dst_real) == 0) v3_same_exe = 1;')
g.writeln('\t\tif (!v3_same_exe) {')
g.writeln('\t\t\tFILE* v3_in = fopen(v3_src, "rb");')
g.writeln('\t\t\tif (v3_in != NULL) {')
g.writeln('\t\t\t\tFILE* v3_out = fopen(v3_vexe_target, "wb");')
g.writeln('\t\t\t\tif (v3_out != NULL) {')
g.writeln('\t\t\t\t\tchar v3_buf[65536];')
g.writeln('\t\t\t\t\tsize_t v3_nread = 0;')
g.writeln('\t\t\t\t\twhile ((v3_nread = fread(v3_buf, 1, sizeof(v3_buf), v3_in)) > 0) fwrite(v3_buf, 1, v3_nread, v3_out);')
g.writeln('\t\t\t\t\tfclose(v3_out);')
g.writeln('\t\t\t\t\tchmod(v3_vexe_target, 0755);')
g.writeln('\t\t\t\t}')
g.writeln('\t\t\t\tfclose(v3_in);')
g.writeln('\t\t\t}')
g.writeln('\t\t}')
g.writeln('\t\tif (access(v3_vexe_target, F_OK) == 0) {')
g.writeln('#ifdef _WIN32')
g.writeln('\t\t\t_putenv_s("VEXE", v3_vexe_target);')
g.writeln('#else')
g.writeln('\t\t\tsetenv("VEXE", v3_vexe_target, 1);')
g.writeln('#endif')
g.writeln('\t\t}')
g.writeln('\t}')
}
fn (mut g FlatGen) gen_defers() {
g.gen_defers_from(0)
}
fn (mut g FlatGen) gen_all_defers() {
g.gen_defers()
g.gen_fn_defers()
}
fn (mut g FlatGen) gen_defers_from(start int) {
if g.defers.len == 0 {
return
}
mut i := g.defers.len
for i > start {
i--
defer_body := g.a.nodes[int(g.defers[i])]
g.writeln('{')
g.indent++
for j in 0 .. defer_body.children_count {
g.gen_node(g.a.child(&defer_body, j))
}
g.indent--
g.writeln('}')
}
}
fn (mut g FlatGen) gen_fn_defers() {
if g.fn_defers.len == 0 {
return
}
mut i := g.fn_defers.len
for i > 0 {
i--
defer_id := g.fn_defers[i]
defer_node := g.a.nodes[int(defer_id)]
defer_body := g.a.nodes[int(g.a.child(&defer_node, 0))]
count_name := g.fn_defer_counts[int(defer_id)] or { '0' }
iter_name := '${count_name}_i'
g.writeln('for (int ${iter_name} = 0; ${iter_name} < ${count_name}; ${iter_name}++) {')
g.indent++
for j in 0 .. defer_body.children_count {
g.gen_node(g.a.child(&defer_body, j))
}
g.indent--
g.writeln('}')
}
}
fn (mut g FlatGen) trim_defers(start int) {
if start >= g.defers.len {
return
}
g.defers = g.defers[..start].clone()
}
fn (mut g FlatGen) gen_ierror_from_error_call(node flat.Node) {
fn_node := g.a.child_node(&node, 0)
g.write('(IError){._typ = 0, ._object = NULL, .message = ')
if node.children_count > 1 {
g.gen_expr(g.a.child(&node, 1))
} else {
g.write('_S("")')
}
g.write(', .code = ')
if fn_node.value == 'error_with_code' && node.children_count > 2 {
g.gen_expr(g.a.child(&node, 2))
} else {
g.write('0')
}
g.write('}')
}
fn (mut g FlatGen) gen_optional_error_from_call(ct string, node flat.Node) {
g.write('(${ct}){.ok = false, .err = ')
g.gen_ierror_from_error_call(node)
g.write('}')
}
fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
fn_node := g.a.child_node(&node, 0)
fn_name := fn_node.value
if fn_node.kind == .selector && fn_node.value == 'str' {
base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
clean_type := types.unwrap_pointer(base_type)
if clean_type is types.Enum {
if _ := g.enum_receiver_method_name(clean_type, fn_node.value) {
// Let normal method call generation handle custom enum str methods.
} else {
g.gen_enum_str_call(fn_node, clean_type)
return
}
}
}
if fn_node.kind == .selector && fn_node.value == 'close' {
base_id := g.a.child(fn_node, 0)
base_type := g.tc.resolve_type(base_id)
if base_type is types.Channel {
g.write('sync__Channel__close(')
g.gen_expr(base_id)
g.write(', array_new(sizeof(IError), 0, 0))')
return
}
}
match fn_name {
'new_map' {
if node.typ.starts_with('map[') {
map_type := g.tc.parse_type(node.typ)
if map_type is types.Map {
g.write_new_map(map_type.key_type, map_type.value_type)
return
}
}
g.write('new_map(')
g.gen_call_args(fn_name, node, 1)
g.write(')')
return
}
'panic' {
g.write('v_panic(')
if node.children_count > 1 {
arg_id := g.a.child(&node, 1)
arg_type := g.tc.resolve_type(arg_id)
if arg_type is types.Struct && arg_type.name == 'IError' {
g.gen_expr(arg_id)
g.write('.message')
} else {
g.gen_expr(arg_id)
}
}
g.write(')')
return
}
'error' {
if g.cur_fn_ret_is_optional {
ct := g.optional_type_name(g.cur_fn_ret)
g.gen_optional_error_from_call(ct, node)
} else {
g.gen_ierror_from_error_call(node)
}
return
}
'error_with_code' {
if g.cur_fn_ret_is_optional {
ct := g.optional_type_name(g.cur_fn_ret)
g.gen_optional_error_from_call(ct, node)
} else {
g.gen_ierror_from_error_call(node)
}
return
}
else {
mut is_method := false
mut is_c_call := false
mut method_name := ''
mut base_id := flat.NodeId(0)
if fn_node.kind == .selector {
base := g.a.child_node(fn_node, 0)
base_is_local := if base.kind == .ident {
(g.tc.cur_scope.lookup(base.value) or { types.Type(types.void_) }) !is types.Void
} else {
false
}
if base.kind == .ident && base.value == 'C' {
g.write(fn_node.value)
is_c_call = true
} else if g.is_flag_enum_method(fn_node) {
g.gen_flag_enum_call(node)
return
} else if base.kind == .ident && !base_is_local && base.value in g.modules {
mod := g.modules[base.value]
short_mod := if mod.contains('.') {
mod.all_after_last('.')
} else {
mod
}
full_name := '${short_mod}.${fn_node.value}'
if full_name in g.tc.type_aliases || full_name in g.tc.structs
|| full_name in g.tc.enum_names || full_name in g.tc.sum_types {
target_type := g.tc.parse_type(full_name)
ct := g.tc.c_type(target_type)
if target_type is types.SumType && node.children_count > 1 {
g.gen_sum_cast_expr(target_type, g.a.child(&node, 1))
} else {
g.write('(${ct})(')
for i in 1 .. node.children_count {
if i > 1 {
g.write(', ')
}
g.gen_expr(g.a.child(&node, i))
}
g.write(')')
}
return
}
g.write(c_name(full_name))
g.write('(')
g.gen_call_args(full_name, node, 1)
g.write(')')
return
} else if base.kind == .selector {
inner := g.a.child_node(base, 0)
inner_is_local := if inner.kind == .ident {
(g.tc.cur_scope.lookup(inner.value) or { types.Type(types.void_) }) !is types.Void
} else {
false
}
if inner.kind == .ident && !inner_is_local && inner.value in g.modules {
mod := g.modules[inner.value]
short_mod := if mod.contains('.') {
mod.all_after_last('.')
} else {
mod
}
full_name := '${short_mod}.${base.value}.${fn_node.value}'
g.write(c_name(full_name))
g.write('(')
g.gen_call_args(full_name, node, 1)
g.write(')')
return
} else {
base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
clean_type := types.unwrap_pointer(base_type)
if g.gen_fn_field_call(node, fn_node, base_type) {
return
}
if arr := array_like_type(clean_type) {
g.gen_array_method_call(node, fn_node, arr)
return
}
if clean_type is types.ArrayFixed && fn_node.value == 'bytestr' {
g.write('u8__vstring_with_len((u8*)')
g.gen_expr(g.a.child(fn_node, 0))
len_expr := g.fixed_array_len_value(clean_type)
g.write(', ${len_expr})')
return
}
if clean_type is types.Map {
if fn_node.value == 'delete' {
g.gen_map_delete(node, fn_node, clean_type, base_type)
return
} else if fn_node.value == 'clone' {
g.write('map__clone(')
g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
g.write(')')
return
} else if fn_node.value == 'clear' {
g.write('map__clear(')
g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
g.write(')')
return
} else if fn_node.value == 'free' {
g.write('map__free(')
if base_type is types.Pointer {
g.gen_expr(g.a.child(fn_node, 0))
} else {
g.write('&')
g.gen_expr(g.a.child(fn_node, 0))
}
g.write(')')
return
}
}
if clean_type is types.String {
method_name = 'string.${fn_node.value}'
if method_name in g.tc.fn_param_types {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(method_name))
} else {
g.write('string__${fn_node.value}(')
g.gen_expr(g.a.child(fn_node, 0))
for i in 1 .. node.children_count {
g.write(', ')
g.gen_expr(g.a.child(&node, i))
}
g.write(')')
return
}
}
if !is_method && (clean_type is types.Primitive
|| clean_type is types.ISize || clean_type is types.USize
|| clean_type is types.Rune) {
tname := clean_type.name()
prim_method := '${tname}.${fn_node.value}'
if prim_method in g.tc.fn_param_types {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(prim_method))
} else {
mut prim_found := false
if alias_method := g.find_alias_method(tname, fn_node.value) {
is_method = true
prim_found = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(alias_method))
}
if !prim_found {
alt_name := g.find_prim_method(fn_node.value)
if alt_name.len > 0 {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(alt_name)
}
}
}
}
if !is_method {
mut struct_name := clean_type.name()
if clean_type is types.Struct {
struct_name = clean_type.name
}
method_name = '${struct_name}.${fn_node.value}'
if method_name !in g.tc.fn_param_types {
for alias, target in g.tc.type_aliases {
if target == struct_name {
alias_method := '${alias}.${fn_node.value}'
if alias_method in g.tc.fn_param_types {
method_name = alias_method
break
}
}
}
}
if method_name in g.tc.fn_param_types {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(method_name))
} else {
str_method := 'string.${fn_node.value}'
if str_method in g.tc.fn_param_types {
is_method = true
method_name = str_method
base_id = g.a.child(fn_node, 0)
g.write(c_name(str_method))
} else if struct_name.len > 0 {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(method_name))
} else {
g.gen_expr(g.a.child(&node, 0))
}
}
}
}
} else if base.kind == .ident
&& (base.value in g.tc.structs || base.value in g.tc.enum_names || g.tc.qualify_name(base.value) in g.tc.structs
|| g.tc.qualify_name(base.value) in g.tc.enum_names) {
qname := if base.value in g.tc.structs || base.value in g.tc.enum_names {
base.value
} else {
g.tc.qualify_name(base.value)
}
static_name := '${qname}.${fn_node.value}'
g.write(c_name(static_name))
g.write('(')
for i in 1 .. node.children_count {
if i > 1 {
g.write(', ')
}
g.gen_expr(g.a.child(&node, i))
}
g.write(')')
return
} else {
base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
clean_type := types.unwrap_pointer(base_type)
if g.gen_fn_field_call(node, fn_node, base_type) {
return
}
if arr := array_like_type(clean_type) {
g.gen_array_method_call(node, fn_node, arr)
return
}
if clean_type is types.ArrayFixed && fn_node.value == 'bytestr' {
g.write('u8__vstring_with_len((u8*)')
g.gen_expr(g.a.child(fn_node, 0))
len_expr := g.fixed_array_len_value(clean_type)
g.write(', ${len_expr})')
return
}
if clean_type is types.Map {
if fn_node.value == 'delete' {
g.gen_map_delete(node, fn_node, clean_type, base_type)
return
} else if fn_node.value == 'clone' {
g.write('map__clone(')
g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
g.write(')')
return
} else if fn_node.value == 'clear' {
g.write('map__clear(')
g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
g.write(')')
return
} else if fn_node.value == 'free' {
g.write('map__free(')
if base_type is types.Pointer {
g.gen_expr(g.a.child(fn_node, 0))
} else {
g.write('&')
g.gen_expr(g.a.child(fn_node, 0))
}
g.write(')')
return
}
}
if clean_type is types.String {
method_name = 'string.${fn_node.value}'
if method_name in g.tc.fn_param_types {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(method_name))
} else {
g.write('string__${fn_node.value}(')
g.gen_expr(g.a.child(fn_node, 0))
for i in 1 .. node.children_count {
g.write(', ')
g.gen_expr(g.a.child(&node, i))
}
g.write(')')
return
}
}
if !is_method && (clean_type is types.Void || clean_type is types.Primitive)
&& fn_node.value in ['vstring', 'vstring_with_len'] {
g.write('u8__${fn_node.value}((u8*)')
g.gen_expr(g.a.child(fn_node, 0))
for i in 1 .. node.children_count {
g.write(', ')
g.gen_expr(g.a.child(&node, i))
}
g.write(')')
return
}
if !is_method && clean_type is types.Struct && clean_type.name == 'IError' {
if fn_node.value == 'msg' {
g.gen_expr(g.a.child(fn_node, 0))
g.write('.message')
return
} else if fn_node.value == 'code' {
g.gen_expr(g.a.child(fn_node, 0))
g.write('.code')
return
}
}
if !is_method && clean_type is types.Struct && clean_type.name == 'array'
&& fn_node.value == 'free' {
g.write('array__free(')
if base_type is types.Pointer {
g.gen_expr(g.a.child(fn_node, 0))
} else {
g.write('&')
g.gen_expr(g.a.child(fn_node, 0))
}
g.write(')')
return
}
if !is_method && (clean_type is types.Primitive
|| clean_type is types.ISize || clean_type is types.USize
|| clean_type is types.Rune) {
tname := clean_type.name()
prim_method := '${tname}.${fn_node.value}'
if prim_method in g.tc.fn_param_types {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(prim_method))
} else {
mut prim_found := false
if alias_method := g.find_alias_method(tname, fn_node.value) {
is_method = true
prim_found = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(alias_method))
}
if !prim_found {
alt_name := g.find_prim_method(fn_node.value)
if alt_name.len > 0 {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(alt_name)
}
}
}
}
if !is_method {
mut struct_name := clean_type.name()
if clean_type is types.Struct {
struct_name = clean_type.name
}
method_name = '${struct_name}.${fn_node.value}'
if method_name !in g.tc.fn_param_types {
for alias, target in g.tc.type_aliases {
if target == struct_name {
alias_method := '${alias}.${fn_node.value}'
if alias_method in g.tc.fn_param_types {
method_name = alias_method
break
}
}
}
}
if method_name in g.tc.fn_param_types {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(method_name))
} else {
str_method := 'string.${fn_node.value}'
if str_method in g.tc.fn_param_types {
is_method = true
method_name = str_method
base_id = g.a.child(fn_node, 0)
g.write(c_name(str_method))
} else if struct_name.len > 0 {
is_method = true
base_id = g.a.child(fn_node, 0)
g.write(c_name(method_name))
} else {
g.gen_expr(g.a.child(&node, 0))
}
}
}
// !is_method
}
} else {
fn_id := g.a.child(&node, 0)
fn_ident := g.a.nodes[int(fn_id)]
if fn_ident.kind == .ident {
qname := g.tc.qualify_name(fn_ident.value)
if fn_ident.value in g.tc.type_aliases || qname in g.tc.type_aliases
|| fn_ident.value in g.tc.structs || qname in g.tc.structs
|| fn_ident.value in g.tc.enum_names || qname in g.tc.enum_names
|| fn_ident.value in g.tc.sum_types || qname in g.tc.sum_types {
type_name := if fn_ident.value in g.tc.type_aliases
|| fn_ident.value in g.tc.structs || fn_ident.value in g.tc.enum_names
|| fn_ident.value in g.tc.sum_types {
fn_ident.value
} else {
qname
}
target_type := g.tc.parse_type(type_name)
ct := g.tc.c_type(target_type)
if target_type is types.SumType && node.children_count > 1 {
g.gen_sum_cast_expr(target_type, g.a.child(&node, 1))
} else {
g.write('(${ct})(')
for i in 1 .. node.children_count {
if i > 1 {
g.write(', ')
}
g.gen_expr(g.a.child(&node, i))
}
g.write(')')
}
return
}
call_key := g.call_key(id, fn_ident.value)
if call_key in g.tc.fn_ret_types || call_key in g.tc.fn_param_types {
g.write(g.direct_call_name(call_key))
} else {
g.write(g.direct_call_name(fn_ident.value))
}
} else {
g.gen_expr(fn_id)
}
}
g.write('(')
actual_fn := if is_method {
method_name
} else {
g.call_key(id, fn_name)
}
param_types := g.param_types_for(actual_fn, fn_name)
mut arg_start := 1
if is_method {
base_type := g.receiver_base_type(base_id)
is_ptr_base := base_type is types.Pointer
wants_ptr := param_types.len > 0 && param_types[0] is types.Pointer
if wants_ptr && !is_ptr_base {
g.write('&')
} else if !wants_ptr && is_ptr_base {
g.write('*')
}
g.gen_expr(base_id)
arg_start = 1
}
num_call_args := node.children_count - arg_start
is_variadic_fn := !is_method && !is_c_call && (g.tc.fn_variadic[actual_fn] or { false })
variadic_idx := if is_variadic_fn && param_types.len > 0
&& param_types[param_types.len - 1] is types.Array {
param_types.len - 1
} else {
-1
}
for i in arg_start .. node.children_count {
if is_method || i > 1 {
g.write(', ')
}
arg_idx := if is_method { i } else { i - 1 }
arg_id := g.a.child(&node, i)
arg_node := g.a.nodes[int(arg_id)]
if arg_node.kind == .field_init {
// `@[params]` struct argument: trailing `key: value` args form a struct literal
ptyp := if arg_idx < param_types.len {
param_types[arg_idx]
} else {
types.Type(types.void_)
}
g.gen_params_struct_arg(ptyp, node, i)
break
}
if !is_method && actual_fn == 'array_push_many' && arg_idx == 1
&& arg_node.kind == .array_literal {
elem_type := if arg_node.children_count > 0 {
g.tc.resolve_type(g.a.child(&arg_node, 0))
} else {
types.Type(types.int_)
}
g.gen_array_literal_value(arg_node, elem_type)
continue
}
if variadic_idx >= 0 && arg_idx == variadic_idx {
variadic_type := param_types[variadic_idx]
if variadic_type is types.Array {
if num_call_args > param_types.len {
c_elem := g.tc.c_type(variadic_type.elem_type)
count := num_call_args - variadic_idx
g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
for j in i .. node.children_count {
if j > i {
g.write(', ')
}
g.gen_expr_with_expected_type(g.a.child(&node, j),
variadic_type.elem_type)
}
g.write('})')
break
}
arg_type := g.tc.resolve_type(arg_id)
if arg_type !is types.Array {
c_elem := g.tc.c_type(variadic_type.elem_type)
g.write('new_array_from_c_array(1, 1, sizeof(${c_elem}), (${c_elem}[]){')
g.gen_expr_with_expected_type(arg_id, variadic_type.elem_type)
g.write('})')
continue
}
}
}
mut needs_addr := false
if !is_c_call && arg_idx < param_types.len && param_types[arg_idx] is types.Pointer
&& !(arg_node.kind == .prefix && arg_node.op == .amp) {
arg_type := g.tc.resolve_type(arg_id)
if arg_type !is types.Pointer {
needs_addr = true
}
}
if !is_c_call && arg_idx < param_types.len {
pt := param_types[arg_idx]
if pt is types.Enum {
g.expected_enum = pt.name
}
}
is_rvalue := arg_node.kind == .call
|| (arg_node.kind == .index && arg_node.value == 'range')
if needs_addr && is_rvalue {
pt := param_types[arg_idx]
ct := g.tc.c_type(types.unwrap_pointer(pt))
g.write('({${ct} _t${g.tmp_count} = ')
g.gen_expr_with_expected_type(arg_id, types.unwrap_pointer(pt))
g.write('; &_t${g.tmp_count};})')
g.tmp_count++
} else {
if needs_addr {
g.write('&')
}
emitted_variant := !needs_addr && !is_c_call && arg_idx < param_types.len
&& g.gen_sum_variant_arg(arg_id, param_types[arg_idx])
if !emitted_variant {
if !is_c_call && arg_idx < param_types.len
&& g.gen_optional_arg(arg_id, param_types[arg_idx]) {
// handled
} else if !is_c_call && arg_idx < param_types.len {
g.gen_expr_with_expected_type(arg_id, param_types[arg_idx])
} else {
g.gen_expr(arg_id)
}
}
}
g.expected_enum = ''
}
actual_args := node.children_count - arg_start
expected_args := if is_method {
param_types.len - 1
} else {
param_types.len
}
if !is_c_call && expected_args > 0 && actual_args < expected_args {
for pi in actual_args .. expected_args {
if is_method || pi > 0 {
g.write(', ')
}
pidx := if is_method { pi + 1 } else { pi }
pt := param_types[pidx]
g.gen_default_value_for_type(pt)
}
}
g.write(')')
}
}
}
fn (g &FlatGen) receiver_base_type(base_id flat.NodeId) types.Type {
if int(base_id) < 0 {
return types.Type(types.void_)
}
base := g.a.nodes[int(base_id)]
if base.kind == .ident {
if typ := g.current_param_type(base.value) {
return typ
}
if typ := g.cur_param_types[base.value] {
return typ
}
if typ := g.tc.cur_scope.lookup(base.value) {
return typ
}
}
return g.tc.resolve_type(base_id)
}
fn (g &FlatGen) current_param_type(name string) ?types.Type {
for i in 0 .. g.cur_param_names.len {
if g.cur_param_names[i] == name {
return g.cur_param_type_values[i]
}
}
return none
}
fn (mut g FlatGen) gen_enum_str_call(fn_node &flat.Node, enum_type types.Enum) {
fields := g.tc.enum_fields[enum_type.name] or { []string{} }
if fields.len == 0 {
sid := g.intern_string('')
g.write('_str_${sid}')
return
}
g.write('({ int _e${g.tmp_count} = ')
g.gen_expr(g.a.child(fn_node, 0))
g.write('; ')
for field in fields {
ekey := '${enum_type.name}.${field}'
if ekey in g.enum_vals {
val := g.enum_vals[ekey]
sid := g.intern_string(field)
g.write('_e${g.tmp_count} == ${val} ? _str_${sid} : ')
}
}
unknown := g.intern_string('')
g.write('_str_${unknown}; })')
g.tmp_count++
}
fn (g &FlatGen) enum_receiver_method_name(enum_type types.Enum, method string) ?string {
name := enum_type.name
direct := '${name}.${method}'
if direct in g.tc.fn_param_types {
return direct
}
short_name := name.all_after_last('.')
if short_name != name {
short_method := '${short_name}.${method}'
if short_method in g.tc.fn_param_types {
return short_method
}
for candidate, _ in g.tc.fn_param_types {
if candidate.ends_with('.${short_method}') {
return candidate
}
}
return none
}
if !name.contains('.') {
for candidate, _ in g.tc.fn_param_types {
if candidate.ends_with('.${direct}') {
return candidate
}
}
}
return none
}
fn (mut g FlatGen) gen_fn_field_call(node flat.Node, fn_node &flat.Node, base_type types.Type) bool {
fn_type := g.fn_field_type(base_type, fn_node.value) or { return false }
base_id := g.a.child(fn_node, 0)
base := g.a.nodes[int(base_id)]
needs_paren := base.kind !in [.ident, .selector, .call]
if needs_paren {
g.write('(')
}
g.gen_expr(base_id)
if needs_paren {
g.write(')')
}
if base_type is types.Pointer {
g.write('->')
} else {
g.write('.')
}
g.write(c_name(fn_node.value))
g.write('(')
for i in 1 .. node.children_count {
if i > 1 {
g.write(', ')
}
arg_id := g.a.child(&node, i)
arg_idx := i - 1
if arg_idx < fn_type.params.len {
g.gen_arg_for_expected_type(arg_id, fn_type.params[arg_idx])
} else {
g.gen_expr(arg_id)
}
}
g.write(')')
return true
}
fn (g &FlatGen) call_key(id flat.NodeId, name string) string {
if resolved := g.tc.resolved_call_name(id) {
return g.normalize_call_key(resolved)
}
return g.normalize_call_key(name)
}
fn (g &FlatGen) normalize_call_key(name string) string {
if name.starts_with('main.') {
short_name := name.all_after_last('.')
if short_name in g.tc.fn_param_types || short_name in g.tc.fn_ret_types {
return short_name
}
}
if !name.contains('.') && g.tc.cur_module.len > 0 && g.tc.cur_module != 'main'
&& g.tc.cur_module != 'builtin' {
local := '${g.tc.cur_module}.${name}'
if local in g.tc.fn_param_types || local in g.tc.fn_ret_types {
return local
}
}
if name in g.tc.fn_param_types || name in g.tc.fn_ret_types {
return name
}
qname := g.tc.qualify_fn_name(name)
if qname in g.tc.fn_param_types || qname in g.tc.fn_ret_types {
return qname
}
for _, mod_name in g.tc.imports {
imported := '${mod_name}.${name}'
if imported in g.tc.fn_param_types || imported in g.tc.fn_ret_types {
return imported
}
}
return qname
}
fn (mut g FlatGen) param_types_for(name string, fallback string) []types.Type {
decl_types := g.param_types_from_decl(name, fallback)
if decl_types.len > 0 {
return decl_types
}
for candidate in [name, fallback] {
if candidate in g.tc.fn_param_types {
return g.tc.fn_param_types[candidate]
}
short_method := short_receiver_method_name(candidate)
if short_method.len > 0 {
if short_method in g.tc.fn_param_types {
return g.tc.fn_param_types[short_method]
}
}
if candidate.starts_with('main.') {
short_name := candidate.all_after_last('.')
if short_name in g.tc.fn_param_types {
return g.tc.fn_param_types[short_name]
}
}
}
return []types.Type{}
}
fn (mut g FlatGen) param_types_from_decl(name string, fallback string) []types.Type {
if name.contains('.') {
if ptypes := g.fn_decl_param_types[name] {
return ptypes
}
short_method := short_receiver_method_name(name)
if short_method.len > 0 {
if ptypes := g.fn_decl_param_types[short_method] {
return ptypes
}
}
} else {
for candidate in [fallback, name] {
if ptypes := g.fn_decl_param_types[candidate] {
return ptypes
}
}
}
return []types.Type{}
}
fn short_receiver_method_name(name string) string {
if !name.contains('.') {
return ''
}
receiver := name.all_before_last('.')
if !receiver.contains('.') {
return ''
}
return '${receiver.all_after_last('.')}.${name.all_after_last('.')}'
}
fn (mut g FlatGen) gen_arg_for_expected_type(arg_id flat.NodeId, expected types.Type) {
arg_node := g.a.nodes[int(arg_id)]
mut needs_addr := false
if expected is types.Pointer && !(arg_node.kind == .prefix && arg_node.op == .amp) {
arg_type := g.tc.resolve_type(arg_id)
if arg_type !is types.Pointer {
needs_addr = true
}
}
if needs_addr {
g.write('&')
}
if !needs_addr && g.gen_sum_variant_arg(arg_id, expected) {
return
}
if !needs_addr && g.gen_optional_arg(arg_id, expected) {
return
}
g.gen_expr_with_expected_type(arg_id, expected)
}
fn (mut g FlatGen) gen_optional_arg(arg_id flat.NodeId, expected types.Type) bool {
mut base_type := types.Type(types.void_)
if expected is types.OptionType {
base_type = expected.base_type
} else if expected is types.ResultType {
base_type = expected.base_type
} else {
return false
}
if g.expr_is_optional_literal(arg_id, expected) {
g.gen_expr_with_expected_type(g.collapsed_optional_literal(arg_id, expected), expected)
return true
}
arg_type := g.usable_expr_type(arg_id)
if arg_type is types.OptionType || arg_type is types.ResultType {
if g.type_names_match(arg_type, expected) {
g.gen_expr_with_expected_type(arg_id, expected)
return true
}
arg_node := g.a.nodes[int(arg_id)]
if arg_node.kind == .none_expr || g.expr_really_returns_optional(arg_id) {
g.gen_expr_with_expected_type(arg_id, expected)
return true
}
}
ct := g.optional_type_name(expected)
if base_type is types.Void {
g.write('(${ct}){.ok = true}')
return true
}
g.write('(${ct}){.ok = true, .value = ')
g.gen_expr_with_expected_type(arg_id, base_type)
g.write('}')
return true
}
fn (mut g FlatGen) expr_is_optional_literal(id flat.NodeId, expected types.Type) bool {
if int(id) < 0 {
return false
}
node := g.a.nodes[int(id)]
if node.kind != .struct_init && node.kind != .cast_expr {
return false
}
return node.value.starts_with('?') || node.value.starts_with('!')
|| node.value == g.optional_type_name(expected) || node.value.starts_with('Optional')
}
fn (mut g FlatGen) collapsed_optional_literal(id flat.NodeId, expected types.Type) flat.NodeId {
mut current := id
for _ in 0 .. 4 {
value_id := g.optional_literal_value_id(current) or { break }
if !g.expr_is_optional_literal(value_id, expected) {
break
}
current = value_id
}
return current
}
fn (g &FlatGen) optional_literal_value_id(id flat.NodeId) ?flat.NodeId {
if int(id) < 0 {
return none
}
node := g.a.nodes[int(id)]
if node.kind != .struct_init && node.kind != .cast_expr {
return none
}
for i in 0 .. node.children_count {
field := g.a.child_node(&node, i)
if field.kind == .field_init && field.value == 'value' && field.children_count > 0 {
return g.a.child(field, 0)
}
}
return none
}
fn (g &FlatGen) fn_field_type(base_type types.Type, field_name string) ?types.FnType {
field_type := g.field_type(base_type, field_name) or { return none }
return fn_type_from(field_type)
}
fn (g &FlatGen) field_type(base_type types.Type, field_name string) ?types.Type {
clean0 := types.unwrap_pointer(base_type)
mut clean := clean0
if clean0 is types.Alias {
clean = clean0.base_type
}
mut struct_name := ''
if clean is types.Struct {
struct_name = clean.name
} else if clean is types.Array {
struct_name = 'array'
} else if clean is types.Map {
struct_name = 'map'
} else if clean is types.String {
struct_name = 'string'
}
if struct_name.len == 0 {
return none
}
fields := g.tc.structs[struct_name] or { return none }
for field in fields {
if field.name == field_name {
return field.typ
}
}
return none
}
fn fn_type_from(t types.Type) ?types.FnType {
if t is types.FnType {
return t
}
if t is types.Alias {
return fn_type_from(t.base_type)
}
return none
}
fn (mut g FlatGen) gen_call_args(fn_name string, node flat.Node, start int) {
mut param_types := []types.Type{}
if fn_name in g.tc.fn_param_types {
param_types = g.tc.fn_param_types[fn_name]
}
is_variadic_fn := g.tc.fn_variadic[fn_name] or { false }
variadic_idx := if is_variadic_fn && param_types.len > 0
&& param_types[param_types.len - 1] is types.Array {
param_types.len - 1
} else {
-1
}
num_args := node.children_count - start
is_variadic := variadic_idx >= 0 && num_args > param_types.len
for i in start .. node.children_count {
if i > start {
g.write(', ')
}
arg_idx := i - start
arg_id := g.a.child(&node, i)
arg_node := g.a.nodes[int(arg_id)]
if arg_node.kind == .field_init {
// `@[params]` struct argument: trailing `key: value` args form a struct literal
ptyp := if arg_idx < param_types.len {
param_types[arg_idx]
} else {
types.Type(types.void_)
}
g.gen_params_struct_arg(ptyp, node, i)
break
}
if fn_name == 'array_push_many' && arg_idx == 1 && arg_node.kind == .array_literal {
elem_type := if arg_node.children_count > 0 {
g.tc.resolve_type(g.a.child(&arg_node, 0))
} else {
types.Type(types.int_)
}
g.gen_array_literal_value(arg_node, elem_type)
continue
}
if is_variadic && arg_idx == variadic_idx {
variadic_type := param_types[variadic_idx]
if variadic_type is types.Array {
c_elem := g.tc.c_type(variadic_type.elem_type)
count := num_args - variadic_idx
g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
for j in i .. node.children_count {
if j > i {
g.write(', ')
}
g.gen_expr_with_expected_type(g.a.child(&node, j), variadic_type.elem_type)
}
g.write('})')
}
break
}
if variadic_idx >= 0 && arg_idx == variadic_idx && num_args == param_types.len {
arg_type := g.tc.resolve_type(arg_id)
if arg_type !is types.Array {
variadic_type := param_types[variadic_idx]
if variadic_type is types.Array {
c_elem := g.tc.c_type(variadic_type.elem_type)
g.write('new_array_from_c_array(1, 1, sizeof(${c_elem}), (${c_elem}[]){')
g.gen_expr_with_expected_type(arg_id, variadic_type.elem_type)
g.write('})')
continue
}
}
}
mut needs_addr := false
if arg_idx < param_types.len && param_types[arg_idx] is types.Pointer
&& !(arg_node.kind == .prefix && arg_node.op == .amp) {
arg_type := g.tc.resolve_type(arg_id)
if arg_type !is types.Pointer {
needs_addr = true
}
}
is_rvalue := arg_node.kind == .call
|| (arg_node.kind == .index && arg_node.value == 'range')
if needs_addr && is_rvalue {
pt := param_types[arg_idx]
ct := g.tc.c_type(types.unwrap_pointer(pt))
g.write('({${ct} _t${g.tmp_count} = ')
g.gen_expr_with_expected_type(arg_id, types.unwrap_pointer(pt))
g.write('; &_t${g.tmp_count};})')
g.tmp_count++
} else {
if needs_addr {
g.write('&')
}
emitted_variant := !needs_addr && arg_idx < param_types.len
&& g.gen_sum_variant_arg(arg_id, param_types[arg_idx])
if !emitted_variant {
if arg_idx < param_types.len && g.gen_optional_arg(arg_id, param_types[arg_idx]) {
// handled
} else if arg_idx < param_types.len {
g.gen_expr_with_expected_type(arg_id, param_types[arg_idx])
} else {
g.gen_expr(arg_id)
}
}
}
if variadic_idx >= 0 && num_args == variadic_idx {
if node.children_count > start {
g.write(', ')
}
variadic_type := param_types[variadic_idx]
if variadic_type is types.Array {
c_elem := g.tc.c_type(variadic_type.elem_type)
g.write('new_array_from_c_array(0, 0, sizeof(${c_elem}), (${c_elem}[]){0})')
}
}
}
num_provided := node.children_count - start
if num_provided < param_types.len {
for i in num_provided .. param_types.len {
if num_provided > 0 || i > num_provided {
g.write(', ')
}
g.gen_default_value_for_type(param_types[i])
}
}
}
fn (g &FlatGen) is_flag_enum_method(fn_node &flat.Node) bool {
if fn_node.kind != .selector {
return false
}
method := fn_node.value
if method !in ['has', 'all', 'set', 'clear'] {
return false
}
base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
clean := types.unwrap_pointer(base_type)
if clean is types.Enum {
return true
} else if clean is types.Primitive {
return clean.props.has(.integer)
} else if clean is types.Unknown {
return true
}
return false
}
fn (mut g FlatGen) gen_flag_enum_call(node flat.Node) {
fn_node := g.a.child_node(&node, 0)
method := fn_node.value
base_id := g.a.child(fn_node, 0)
base_type := types.unwrap_pointer(g.tc.resolve_type(base_id))
match method {
'has' {
g.write('((')
g.gen_expr(base_id)
g.write(' & ')
if node.children_count > 1 {
g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
}
g.write(') != 0)')
}
'all' {
g.write('((')
g.gen_expr(base_id)
g.write(' & (')
if node.children_count > 1 {
g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
}
g.write(')) == (')
if node.children_count > 1 {
g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
}
g.write('))')
}
'set' {
g.gen_expr(base_id)
g.write(' |= ')
if node.children_count > 1 {
g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
}
}
'clear' {
g.gen_expr(base_id)
g.write(' &= ~(')
if node.children_count > 1 {
g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
}
g.write(')')
}
else {}
}
}
fn (mut g FlatGen) gen_flag_enum_arg(arg_id flat.NodeId, base_type types.Type) {
if base_type is types.Enum {
g.gen_expr_with_expected_type(arg_id, base_type)
} else {
g.gen_expr(arg_id)
}
}
fn is_generic_type(typ string) bool {
t := typ.trim_left('&?!')
return t.len == 1 && t[0] >= `A` && t[0] <= `Z`
}
fn (g &FlatGen) has_generic_params(node flat.Node) bool {
for i in 0 .. node.children_count {
child := g.a.child_node(&node, i)
if child.kind == .param && is_generic_type(child.typ) {
return true
}
}
return is_generic_type(node.typ)
}
fn (g &FlatGen) find_prim_method(method string) string {
if 'u8.${method}' in g.tc.fn_param_types {
return c_name('u8.${method}')
}
if 'int.${method}' in g.tc.fn_param_types {
return c_name('int.${method}')
}
if 'i64.${method}' in g.tc.fn_param_types {
return c_name('i64.${method}')
}
if 'u32.${method}' in g.tc.fn_param_types {
return c_name('u32.${method}')
}
if 'u64.${method}' in g.tc.fn_param_types {
return c_name('u64.${method}')
}
return ''
}
fn (g &FlatGen) find_alias_method(target string, method string) ?string {
mut fallback := ''
for alias, alias_target in g.tc.type_aliases {
if alias_target != target {
continue
}
alias_method := '${alias}.${method}'
if alias_method !in g.tc.fn_param_types {
if alias.contains('.') {
short_method := '${alias.all_after_last('.')}.${method}'
if short_method in g.tc.fn_param_types {
return alias_method
}
}
continue
}
if alias.contains('.') {
return alias_method
}
if fallback.len == 0 {
fallback = alias_method
}
}
if fallback.len > 0 {
return fallback
}
return none
}
fn (mut g FlatGen) gen_sum_variant_arg(arg_id flat.NodeId, expected types.Type) bool {
actual0 := types.unwrap_pointer(g.tc.resolve_type(arg_id))
mut actual := actual0
if actual0 is types.Alias {
actual = actual0.base_type
}
expected0 := expected
mut expected_type := expected0
if expected0 is types.Alias {
expected_type = expected0.base_type
}
if expected_type is types.SumType {
return false
}
if actual !is types.SumType {
return false
}
sum_type := actual as types.SumType
sum_name := sum_type.name
variant := g.resolve_variant(sum_name, expected_type.name())
variants := g.tc.sum_types[sum_name] or { return false }
if variant !in variants {
return false
}
is_ptr_arg := g.tc.resolve_type(arg_id) is types.Pointer
is_ref_variant := g.variant_references_sum(variant, sum_name)
if is_ref_variant {
g.write('(*')
}
g.gen_expr(arg_id)
if is_ptr_arg {
g.write('->')
} else {
g.write('.')
}
g.write(g.sum_field_name(variant))
if is_ref_variant {
g.write(')')
}
return true
}
fn (mut g FlatGen) forward_decls() {
mut cur_module := ''
for i in 0 .. g.a.nodes.len {
node := g.a.nodes[i]
kind_id := node_kind_id(node)
if kind_id == 77 {
cur_module = ''
g.tc.cur_module = cur_module
continue
}
if kind_id == 73 {
cur_module = node.value
g.tc.cur_module = cur_module
continue
}
is_entry_main := node.value == 'main' && (cur_module.len == 0 || cur_module == 'main')
if kind_id == 61 && !is_entry_main {
if !g.should_emit_fn_node_in_module(node, i, cur_module) {
continue
}
qfn := qualified_fn_name_in_module(cur_module, node.value)
g.tc.cur_module = cur_module
ret_type := g.tc.parse_type(node.typ)
g.write(g.optional_type_name(ret_type))
g.write(' ')
g.write(qfn)
g.write('(')
g.write_fn_node_params(node)
g.writeln(');')
} else if kind_id == 76
&& (node.value.starts_with('C.v_filelock_') || node.value.starts_with('v_filelock_')) {
g.tc.cur_module = cur_module
ret_type := g.tc.parse_type(node.typ)
g.write(g.optional_type_name(ret_type))
g.write(' ')
g.write(c_name(node.value))
g.write('(')
g.write_c_fn_node_params(node)
g.writeln(');')
}
}
g.writeln('')
}
fn (mut g FlatGen) write_fn_node_params(node flat.Node) {
mut params_len := 0
for i in 0 .. node.children_count {
if g.a.child_node(&node, i).kind == .param {
params_len++
}
}
if params_len == 0 {
g.write('void')
return
}
mut written := 0
for i in 0 .. node.children_count {
param_id := g.a.child(&node, i)
p := g.a.node(param_id)
if p.kind != .param {
continue
}
pt := g.tc.parse_type(p.typ)
ct := if pt is types.OptionType || pt is types.ResultType {
g.optional_type_name(pt)
} else {
g.tc.c_type(pt)
}
if ct.starts_with('fn_ptr:') {
g.write(g.resolve_fn_ptr_type(ct))
} else {
g.write(ct)
}
if p.value.len > 0 {
g.write(' ')
param_name := if p.value == '_' { '_${written}' } else { c_name(p.value) }
g.write(param_name)
}
written++
if written < params_len {
g.write(', ')
}
}
}
fn (mut g FlatGen) write_c_fn_node_params(node flat.Node) {
if node.children_count == 0 {
g.write('void')
return
}
mut written := 0
for i in 0 .. node.children_count {
param_id := g.a.child(&node, i)
p := g.a.node(param_id)
if p.kind != .param {
continue
}
raw_typ := if p.typ.len > 0 { p.typ } else { p.value }
if raw_typ.len == 0 {
continue
}
pt := g.tc.parse_type(raw_typ)
ct := if pt is types.OptionType || pt is types.ResultType {
g.optional_type_name(pt)
} else {
g.tc.c_type(pt)
}
if written > 0 {
g.write(', ')
}
if ct.starts_with('fn_ptr:') {
g.write(g.resolve_fn_ptr_type(ct))
} else {
g.write(ct)
}
if p.typ.len > 0 && p.value.len > 0 {
g.write(' ')
param_name := if p.value == '_' { '_${written}' } else { c_name(p.value) }
g.write(param_name)
}
written++
}
if written == 0 {
g.write('void')
}
}
fn (mut g FlatGen) fn_ptr_typedefs() {
for encoded, name in g.fn_ptr_types {
parts := encoded['fn_ptr:'.len..].split('|')
ret := parts[0]
params := if parts.len > 1 { parts[1] } else { 'void' }
g.writeln('typedef ${ret} (*${name})(${params});')
}
if g.fn_ptr_types.len > 0 {
g.writeln('')
}
}
fn (mut g FlatGen) multi_return_typedefs() {
mut emitted := map[string]bool{}
for _, ret in g.tc.fn_ret_types {
g.emit_multi_return_typedef(ret, mut emitted)
}
if emitted.len > 0 {
g.writeln('')
}
}
fn (mut g FlatGen) emit_multi_return_typedef(ret types.Type, mut emitted map[string]bool) {
if ret is types.OptionType {
g.emit_multi_return_typedef(ret.base_type, mut emitted)
return
}
if ret is types.ResultType {
g.emit_multi_return_typedef(ret.base_type, mut emitted)
return
}
if ret is types.MultiReturn {
name := g.tc.c_type(ret)
if name in emitted {
return
}
emitted[name] = true
g.writeln('typedef struct {')
for i, typ in ret.types {
g.writeln('\t${g.tc.c_type(typ)} arg${i};')
}
g.writeln('} ${name};')
}
}
fn (mut g FlatGen) resolve_fn_ptr_type(typ string) string {
if typ in g.fn_ptr_types {
return g.fn_ptr_types[typ]
}
name := '_fn_ptr_${g.fn_ptr_types.len}'
g.fn_ptr_types[typ] = name
return name
}