1891 lines
52 KiB
V
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
|
|
}
|