From 79117f5581232ac750a7c04a7c2dfad7cae6f3bf Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 27 Dec 2020 18:24:35 +0800 Subject: [PATCH] cgen: move logic from cgen.v to auto_eq_methods.v and array.v (#7572) --- vlib/v/gen/array.v | 531 ++++++++++++++++++++++++++++ vlib/v/gen/auto_eq_methods.v | 144 ++++++++ vlib/v/gen/cgen.v | 662 ----------------------------------- 3 files changed, 675 insertions(+), 662 deletions(-) create mode 100644 vlib/v/gen/array.v create mode 100644 vlib/v/gen/auto_eq_methods.v diff --git a/vlib/v/gen/array.v b/vlib/v/gen/array.v new file mode 100644 index 0000000000..319bdfae55 --- /dev/null +++ b/vlib/v/gen/array.v @@ -0,0 +1,531 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module gen + +import strings +import v.ast +import v.table + +fn (mut g Gen) array_init(it ast.ArrayInit) { + type_sym := g.table.get_type_symbol(it.typ) + styp := g.typ(it.typ) + mut shared_styp := '' // only needed for shared &[]{...} + is_amp := g.is_amp + g.is_amp = false + if is_amp { + g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + if g.is_shared { + mut shared_typ := it.typ.set_flag(.shared_f) + shared_styp = g.typ(shared_typ) + g.writeln('($shared_styp*)memdup(&($shared_styp){.val = ') + } else { + g.write('($styp*)memdup(&') // TODO: doesn't work with every compiler + } + } else { + if g.is_shared { + g.writeln('{.val = ($styp*)') + } + } + if type_sym.kind == .array_fixed { + g.write('{') + if it.has_val { + for i, expr in it.exprs { + g.expr(expr) + if i != it.exprs.len - 1 { + g.write(', ') + } + } + } else { + g.write('0') + } + g.write('}') + return + } + elem_type_str := g.typ(it.elem_type) + if it.exprs.len == 0 { + elem_sym := g.table.get_type_symbol(it.elem_type) + is_default_array := elem_sym.kind == .array && it.has_default + if is_default_array { + g.write('__new_array_with_array_default(') + } else { + g.write('__new_array_with_default(') + } + if it.has_len { + g.expr(it.len_expr) + g.write(', ') + } else { + g.write('0, ') + } + if it.has_cap { + g.expr(it.cap_expr) + g.write(', ') + } else { + g.write('0, ') + } + if elem_sym.kind == .function { + g.write('sizeof(voidptr), ') + } else { + g.write('sizeof($elem_type_str), ') + } + if is_default_array { + g.write('($elem_type_str[]){') + g.expr(it.default_expr) + g.write('}[0])') + } else if it.has_default { + g.write('&($elem_type_str[]){') + g.expr(it.default_expr) + g.write('})') + } else if it.has_len && it.elem_type == table.string_type { + g.write('&($elem_type_str[]){') + g.write('_SLIT("")') + g.write('})') + } else { + g.write('0)') + } + return + } + len := it.exprs.len + elem_sym := g.table.get_type_symbol(it.elem_type) + if elem_sym.kind == .function { + g.write('new_array_from_c_array($len, $len, sizeof(voidptr), _MOV((voidptr[$len]){') + } else { + g.write('new_array_from_c_array($len, $len, sizeof($elem_type_str), _MOV(($elem_type_str[$len]){') + } + if len > 8 { + g.writeln('') + g.write('\t\t') + } + for i, expr in it.exprs { + if it.is_interface { + // sym := g.table.get_type_symbol(it.expr_types[i]) + // isym := g.table.get_type_symbol(it.interface_type) + g.interface_call(it.expr_types[i], it.interface_type) + } + g.expr_with_cast(expr, it.expr_types[i], it.elem_type) + if it.is_interface { + g.write(')') + } + if i != len - 1 { + g.write(', ') + } + } + g.write('}))') + if g.is_shared { + g.write(', .mtx = sync__new_rwmutex()}') + if is_amp { + g.write(', sizeof($shared_styp))') + } + } else if is_amp { + g.write(', sizeof($styp))') + } +} + +// `nums.map(it % 2 == 0)` +fn (mut g Gen) gen_array_map(node ast.CallExpr) { + g.inside_lambda = true + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + // println('filter s="$s"') + ret_typ := g.typ(node.return_type) + // inp_typ := g.typ(node.receiver_type) + ret_sym := g.table.get_type_symbol(node.return_type) + inp_sym := g.table.get_type_symbol(node.receiver_type) + ret_info := ret_sym.info as table.Array + ret_elem_type := g.typ(ret_info.elem_type) + inp_info := inp_sym.info as table.Array + inp_elem_type := g.typ(inp_info.elem_type) + if inp_sym.kind != .array { + verror('map() requires an array') + } + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.write('int ${tmp}_len = ${tmp}_orig.len;') + g.writeln('$ret_typ $tmp = __new_array(0, ${tmp}_len, sizeof($ret_elem_type));') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.write('\t$inp_elem_type it = (($inp_elem_type*) ${tmp}_orig.data)[$i];') + g.write('\t$ret_elem_type ti = ') + expr := node.args[0].expr + match expr { + ast.AnonFn { + g.gen_anon_fn_decl(expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym := g.table.get_type_symbol(var_info.typ) + if sym.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + else { + g.expr(node.args[0].expr) + } + } + g.writeln(';') + g.writeln('\tarray_push(&$tmp, &ti);') + g.writeln('}') + g.write(s) + g.write(tmp) + g.inside_lambda = false +} + +// `users.sort(a.age < b.age)` +fn (mut g Gen) gen_array_sort(node ast.CallExpr) { + // println('filter s="$s"') + rec_sym := g.table.get_type_symbol(node.receiver_type) + if rec_sym.kind != .array { + println(node.name) + println(g.typ(node.receiver_type)) + // println(rec_sym.kind) + verror('.sort() is an array method') + } + info := rec_sym.info as table.Array + // No arguments means we are sorting an array of builtins (e.g. `numbers.sort()`) + // The type for the comparison fns is the type of the element itself. + mut typ := info.elem_type + mut is_reverse := false + // `users.sort(a.age > b.age)` + if node.args.len > 0 { + // Get the type of the field that's being compared + // `a.age > b.age` => `age int` => int + infix_expr := node.args[0].expr as ast.InfixExpr + // typ = infix_expr.left_type + is_reverse = infix_expr.op == .gt + } + mut compare_fn := '' + match typ { + table.int_type { + compare_fn = 'compare_ints' + } + table.u64_type { + compare_fn = 'compare_u64s' + } + table.string_type { + compare_fn = 'compare_strings' + } + table.f64_type { + compare_fn = 'compare_floats' + } + else { + // Generate a comparison function for a custom type + if node.args.len == 0 { + verror('usage: .sort(a.field < b.field)') + } + // verror('sort(): unhandled type $typ $q.name') + tmp_name := g.new_tmp_var() + compare_fn = 'compare_${tmp_name}_' + g.typ(typ) + if is_reverse { + compare_fn += '_reverse' + } + // Register a new custom `compare_xxx` function for qsort() + g.table.register_fn(name: compare_fn, return_type: table.int_type) + infix_expr := node.args[0].expr as ast.InfixExpr + styp := g.typ(typ) + // Variables `a` and `b` are used in the `.sort(a < b)` syntax, so we can reuse them + // when generating the function as long as the args are named the same. + g.definitions.writeln('int $compare_fn ($styp* a, $styp* b) {') + field_type := g.typ(infix_expr.left_type) + left_expr_str := g.write_expr_to_string(infix_expr.left).replace_once('.', + '->') + right_expr_str := g.write_expr_to_string(infix_expr.right).replace_once('.', + '->') + g.definitions.writeln('$field_type a_ = $left_expr_str;') + g.definitions.writeln('$field_type b_ = $right_expr_str;') + mut op1, mut op2 := '', '' + if infix_expr.left_type == table.string_type { + if is_reverse { + op1 = 'string_gt(a_, b_)' + op2 = 'string_lt(a_, b_)' + } else { + op1 = 'string_lt(a_, b_)' + op2 = 'string_gt(a_, b_)' + } + } else { + if is_reverse { + op1 = 'a_ > b_' + op2 = 'a_ < b_' + } else { + op1 = 'a_ < b_' + op2 = 'a_ > b_' + } + } + g.definitions.writeln('if ($op1) return -1;') + g.definitions.writeln('if ($op2) return 1; return 0; }\n') + } + } + if is_reverse && !compare_fn.ends_with('_reverse') { + compare_fn += '_reverse' + } + // + deref := if node.left_type.is_ptr() || node.left_type.is_pointer() { '->' } else { '.' } + // eprintln('> qsort: pointer $node.left_type | deref: `$deref`') + g.write('qsort(') + g.expr(node.left) + g.write('${deref}data, ') + g.expr(node.left) + g.write('${deref}len, ') + g.expr(node.left) + g.writeln('${deref}element_size, (int (*)(const void *, const void *))&$compare_fn);') +} + +// `nums.filter(it % 2 == 0)` +fn (mut g Gen) gen_array_filter(node ast.CallExpr) { + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + // println('filter s="$s"') + sym := g.table.get_type_symbol(node.return_type) + if sym.kind != .array { + verror('filter() requires an array') + } + info := sym.info as table.Array + styp := g.typ(node.return_type) + elem_type_str := g.typ(info.elem_type) + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.write('int ${tmp}_len = ${tmp}_orig.len;') + g.writeln('$styp $tmp = __new_array(0, ${tmp}_len, sizeof($elem_type_str));') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln(' $elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') + g.write('if (') + expr := node.args[0].expr + match expr { + ast.AnonFn { + g.gen_anon_fn_decl(expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym_t := g.table.get_type_symbol(var_info.typ) + if sym_t.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + else { + g.expr(node.args[0].expr) + } + } + g.writeln(') array_push(&$tmp, &it); \n }') + g.write(s) + g.write(' ') + g.write(tmp) +} + +// `nums.insert(0, 2)` `nums.insert(0, [2,3,4])` +fn (mut g Gen) gen_array_insert(node ast.CallExpr) { + left_sym := g.table.get_type_symbol(node.left_type) + left_info := left_sym.info as table.Array + elem_type_str := g.typ(left_info.elem_type) + arg2_sym := g.table.get_type_symbol(node.args[1].typ) + is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type + if is_arg2_array { + g.write('array_insert_many(&') + } else { + g.write('array_insert(&') + } + g.expr(node.left) + g.write(', ') + g.expr(node.args[0].expr) + if is_arg2_array { + g.write(', ') + g.expr(node.args[1].expr) + g.write('.data, ') + g.expr(node.args[1].expr) + g.write('.len)') + } else { + g.write(', &($elem_type_str[]){') + if left_info.elem_type == table.string_type { + g.write('string_clone(') + } + g.expr(node.args[1].expr) + if left_info.elem_type == table.string_type { + g.write(')') + } + g.write('})') + } +} + +// `nums.prepend(2)` `nums.prepend([2,3,4])` +fn (mut g Gen) gen_array_prepend(node ast.CallExpr) { + left_sym := g.table.get_type_symbol(node.left_type) + left_info := left_sym.info as table.Array + elem_type_str := g.typ(left_info.elem_type) + arg_sym := g.table.get_type_symbol(node.args[0].typ) + is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type + if is_arg_array { + g.write('array_prepend_many(&') + } else { + g.write('array_prepend(&') + } + g.expr(node.left) + if is_arg_array { + g.write(', ') + g.expr(node.args[0].expr) + g.write('.data, ') + g.expr(node.args[0].expr) + g.write('.len)') + } else { + g.write(', &($elem_type_str[]){') + g.expr(node.args[0].expr) + g.write('})') + } +} + +fn (mut g Gen) gen_array_contains_method(left_type table.Type) string { + mut left_sym := g.table.get_type_symbol(left_type) + mut left_type_str := g.typ(left_type).replace('*', '') + left_info := left_sym.info as table.Array + mut elem_type_str := g.typ(left_info.elem_type) + elem_sym := g.table.get_type_symbol(left_info.elem_type) + if elem_sym.kind == .function { + left_type_str = 'array_voidptr' + elem_type_str = 'voidptr' + } + fn_name := '${left_type_str}_contains' + if !left_sym.has_method('contains') { + g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {') + fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {') + match elem_sym.kind { + .string { + fn_builder.writeln('\t\tif (string_eq((*(string*)array_get(a, i)), v)) {') + } + .array { + ptr_typ := g.gen_array_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(*($elem_type_str*)array_get(a, i), v)) {') + } + .function { + fn_builder.writeln('\t\tif ((*(voidptr*)array_get(a, i)) == v) {') + } + .map { + ptr_typ := g.gen_map_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(*($elem_type_str*)array_get(a, i), v)) {') + } + .struct_ { + ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(*($elem_type_str*)array_get(a, i), v)) {') + } + else { + fn_builder.writeln('\t\tif ((*($elem_type_str*)array_get(a, i)) == v) {') + } + } + fn_builder.writeln('\t\t\treturn true;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn false;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + left_sym.register_method(&table.Fn{ + name: 'contains' + params: [table.Param{ + typ: left_type + }, table.Param{ + typ: left_info.elem_type + }] + }) + } + return fn_name +} + +// `nums.contains(2)` +fn (mut g Gen) gen_array_contains(node ast.CallExpr) { + fn_name := g.gen_array_contains_method(node.left_type) + g.write('${fn_name}(') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + g.expr(node.args[0].expr) + g.write(')') +} + +fn (mut g Gen) gen_array_index_method(left_type table.Type) string { + mut left_sym := g.table.get_type_symbol(left_type) + mut left_type_str := g.typ(left_type).replace('*', '') + left_info := left_sym.info as table.Array + mut elem_type_str := g.typ(left_info.elem_type) + elem_sym := g.table.get_type_symbol(left_info.elem_type) + if elem_sym.kind == .function { + left_type_str = 'array_voidptr' + elem_type_str = 'voidptr' + } + fn_name := '${left_type_str}_index' + if !left_sym.has_method('index') { + g.type_definitions.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v) {') + fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {') + match elem_sym.kind { + .string { + fn_builder.writeln('\t\tif (string_eq((*(string*)array_get(a, i)), v)) {') + } + .array { + ptr_typ := g.gen_array_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(*($elem_type_str*)array_get(a, i), v)) {') + } + .function { + fn_builder.writeln('\t\tif ((*(voidptr*)array_get(a, i)) == v) {') + } + .map { + ptr_typ := g.gen_map_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(*($elem_type_str*)array_get(a, i), v)) {') + } + .struct_ { + ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(*($elem_type_str*)array_get(a, i), v)) {') + } + else { + fn_builder.writeln('\t\tif ((*($elem_type_str*)array_get(a, i)) == v) {') + } + } + fn_builder.writeln('\t\t\treturn i;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn -1;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + left_sym.register_method(&table.Fn{ + name: 'index' + params: [table.Param{ + typ: left_type + }, table.Param{ + typ: left_info.elem_type + }] + }) + } + return fn_name +} + +// `nums.index(2)` +fn (mut g Gen) gen_array_index(node ast.CallExpr) { + fn_name := g.gen_array_index_method(node.left_type) + g.write('${fn_name}(') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + g.expr(node.args[0].expr) + g.write(')') +} diff --git a/vlib/v/gen/auto_eq_methods.v b/vlib/v/gen/auto_eq_methods.v new file mode 100644 index 0000000000..8fe203a90f --- /dev/null +++ b/vlib/v/gen/auto_eq_methods.v @@ -0,0 +1,144 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module gen + +import strings +import v.table + +fn (mut g Gen) gen_struct_equality_fn(left table.Type) string { + left_sym := g.table.get_type_symbol(left) + info := left_sym.struct_info() + ptr_typ := g.typ(left).trim('*') + if ptr_typ in g.struct_fn_definitions { + return ptr_typ + } + g.struct_fn_definitions << ptr_typ + g.type_definitions.writeln('static bool ${ptr_typ}_struct_eq($ptr_typ a, $ptr_typ b); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_typ}_struct_eq($ptr_typ a, $ptr_typ b) {') + for field in info.fields { + sym := g.table.get_type_symbol(field.typ) + match sym.kind { + .string { + fn_builder.writeln('\tif (string_ne(a.$field.name, b.$field.name)) {') + } + .struct_ { + eq_fn := g.gen_struct_equality_fn(field.typ) + fn_builder.writeln('\tif (!${eq_fn}_struct_eq(a.$field.name, b.$field.name)) {') + } + .array { + eq_fn := g.gen_array_equality_fn(field.typ) + fn_builder.writeln('\tif (!${eq_fn}_arr_eq(a.$field.name, b.$field.name)) {') + } + .map { + eq_fn := g.gen_map_equality_fn(field.typ) + fn_builder.writeln('\tif (!${eq_fn}_map_eq(a.$field.name, b.$field.name)) {') + } + .function { + fn_builder.writeln('\tif (*((voidptr*)(a.$field.name)) != *((voidptr*)(b.$field.name))) {') + } + else { + fn_builder.writeln('\tif (a.$field.name != b.$field.name)) {') + } + } + fn_builder.writeln('\t\treturn false;') + fn_builder.writeln('\t}') + } + fn_builder.writeln('\treturn true;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_typ +} + +fn (mut g Gen) gen_array_equality_fn(left table.Type) string { + left_sym := g.table.get_type_symbol(left) + typ_name := g.typ(left) + ptr_typ := typ_name[typ_name.index_after('_', 0) + 1..].trim('*') + elem_sym := g.table.get_type_symbol(left_sym.array_info().elem_type) + elem_typ := g.typ(left_sym.array_info().elem_type) + ptr_elem_typ := elem_typ[elem_typ.index_after('_', 0) + 1..] + if elem_sym.kind == .array { + // Recursively generate array element comparison function code if array element is array type + g.gen_array_equality_fn(left_sym.array_info().elem_type) + } + if ptr_typ in g.array_fn_definitions { + return ptr_typ + } + g.array_fn_definitions << ptr_typ + g.type_definitions.writeln('static bool ${ptr_typ}_arr_eq(array_$ptr_typ a, array_$ptr_typ b); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_typ}_arr_eq(array_$ptr_typ a, array_$ptr_typ b) {') + fn_builder.writeln('\tif (a.len != b.len) {') + fn_builder.writeln('\t\treturn false;') + fn_builder.writeln('\t}') + i := g.new_tmp_var() + fn_builder.writeln('\tfor (int $i = 0; $i < a.len; ++$i) {') + // compare every pair of elements of the two arrays + match elem_sym.kind { + .string { fn_builder.writeln('\t\tif (string_ne(*(($ptr_typ*)((byte*)a.data+($i*a.element_size))), *(($ptr_typ*)((byte*)b.data+($i*b.element_size))))) {') } + .struct_ { fn_builder.writeln('\t\tif (memcmp((byte*)a.data+($i*a.element_size), (byte*)b.data+($i*b.element_size), a.element_size)) {') } + .array { fn_builder.writeln('\t\tif (!${ptr_elem_typ}_arr_eq((($elem_typ*)a.data)[$i], (($elem_typ*)b.data)[$i])) {') } + .function { fn_builder.writeln('\t\tif (*((voidptr*)((byte*)a.data+($i*a.element_size))) != *((voidptr*)((byte*)b.data+($i*b.element_size)))) {') } + else { fn_builder.writeln('\t\tif (*(($ptr_typ*)((byte*)a.data+($i*a.element_size))) != *(($ptr_typ*)((byte*)b.data+($i*b.element_size)))) {') } + } + fn_builder.writeln('\t\t\treturn false;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn true;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_typ +} + +fn (mut g Gen) gen_map_equality_fn(left table.Type) string { + left_sym := g.table.get_type_symbol(left) + ptr_typ := g.typ(left).trim('*') + value_sym := g.table.get_type_symbol(left_sym.map_info().value_type) + value_typ := g.typ(left_sym.map_info().value_type) + if value_sym.kind == .map { + // Recursively generate map element comparison function code if array element is map type + g.gen_map_equality_fn(left_sym.map_info().value_type) + } + if ptr_typ in g.map_fn_definitions { + return ptr_typ + } + g.map_fn_definitions << ptr_typ + g.type_definitions.writeln('static bool ${ptr_typ}_map_eq($ptr_typ a, $ptr_typ b); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_typ}_map_eq($ptr_typ a, $ptr_typ b) {') + fn_builder.writeln('\tif (a.len != b.len) {') + fn_builder.writeln('\t\treturn false;') + fn_builder.writeln('\t}') + fn_builder.writeln('\tarray_string _keys = map_keys(&a);') + i := g.new_tmp_var() + fn_builder.writeln('\tfor (int $i = 0; $i < _keys.len; ++$i) {') + fn_builder.writeln('\t\tstring k = string_clone( ((string*)_keys.data)[$i]);') + if value_sym.kind == .function { + func := value_sym.info as table.FnType + ret_styp := g.typ(func.func.return_type) + fn_builder.write('\t\t$ret_styp (*v) (') + arg_len := func.func.params.len + for j, arg in func.func.params { + arg_styp := g.typ(arg.typ) + fn_builder.write('$arg_styp $arg.name') + if j < arg_len - 1 { + fn_builder.write(', ') + } + } + fn_builder.writeln(') = (*(voidptr*)map_get_1(&a, &k, &(voidptr[]){ 0 }));') + } else { + fn_builder.writeln('\t\t$value_typ v = (*($value_typ*)map_get_1(&a, &k, &($value_typ[]){ 0 }));') + } + match value_sym.kind { + .string { fn_builder.writeln('\t\tif (!map_exists(b, k) || string_ne((*(string*)map_get_1(&b, &k, &(string[]){_SLIT("")})), v)) {') } + .function { fn_builder.writeln('\t\tif (!map_exists(b, k) || (*(voidptr*)map_get_1(&b, &k, &(voidptr[]){ 0 })) != v) {') } + else { fn_builder.writeln('\t\tif (!map_exists(b, k) || (*($value_typ*)map_get_1(&b, &k, &($value_typ[]){ 0 })) != v) {') } + } + fn_builder.writeln('\t\t\treturn false;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn true;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_typ +} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 5127583cdf..c1d7c5d871 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -4533,144 +4533,6 @@ fn (mut g Gen) assoc(node ast.Assoc) { } } -fn (mut g Gen) gen_struct_equality_fn(left table.Type) string { - left_sym := g.table.get_type_symbol(left) - info := left_sym.struct_info() - ptr_typ := g.typ(left).trim('*') - if ptr_typ in g.struct_fn_definitions { - return ptr_typ - } - g.struct_fn_definitions << ptr_typ - g.type_definitions.writeln('static bool ${ptr_typ}_struct_eq($ptr_typ a, $ptr_typ b); // auto') - mut fn_builder := strings.new_builder(512) - fn_builder.writeln('static bool ${ptr_typ}_struct_eq($ptr_typ a, $ptr_typ b) {') - for field in info.fields { - sym := g.table.get_type_symbol(field.typ) - match sym.kind { - .string { - fn_builder.writeln('\tif (string_ne(a.$field.name, b.$field.name)) {') - } - .struct_ { - eq_fn := g.gen_struct_equality_fn(field.typ) - fn_builder.writeln('\tif (!${eq_fn}_struct_eq(a.$field.name, b.$field.name)) {') - } - .array { - eq_fn := g.gen_array_equality_fn(field.typ) - fn_builder.writeln('\tif (!${eq_fn}_arr_eq(a.$field.name, b.$field.name)) {') - } - .map { - eq_fn := g.gen_map_equality_fn(field.typ) - fn_builder.writeln('\tif (!${eq_fn}_map_eq(a.$field.name, b.$field.name)) {') - } - .function { - fn_builder.writeln('\tif (*((voidptr*)(a.$field.name)) != *((voidptr*)(b.$field.name))) {') - } - else { - fn_builder.writeln('\tif (a.$field.name != b.$field.name)) {') - } - } - fn_builder.writeln('\t\treturn false;') - fn_builder.writeln('\t}') - } - fn_builder.writeln('\treturn true;') - fn_builder.writeln('}') - g.auto_fn_definitions << fn_builder.str() - return ptr_typ -} - -fn (mut g Gen) gen_array_equality_fn(left table.Type) string { - left_sym := g.table.get_type_symbol(left) - typ_name := g.typ(left) - ptr_typ := typ_name[typ_name.index_after('_', 0) + 1..].trim('*') - elem_sym := g.table.get_type_symbol(left_sym.array_info().elem_type) - elem_typ := g.typ(left_sym.array_info().elem_type) - ptr_elem_typ := elem_typ[elem_typ.index_after('_', 0) + 1..] - if elem_sym.kind == .array { - // Recursively generate array element comparison function code if array element is array type - g.gen_array_equality_fn(left_sym.array_info().elem_type) - } - if ptr_typ in g.array_fn_definitions { - return ptr_typ - } - g.array_fn_definitions << ptr_typ - g.type_definitions.writeln('static bool ${ptr_typ}_arr_eq(array_$ptr_typ a, array_$ptr_typ b); // auto') - mut fn_builder := strings.new_builder(512) - fn_builder.writeln('static bool ${ptr_typ}_arr_eq(array_$ptr_typ a, array_$ptr_typ b) {') - fn_builder.writeln('\tif (a.len != b.len) {') - fn_builder.writeln('\t\treturn false;') - fn_builder.writeln('\t}') - i := g.new_tmp_var() - fn_builder.writeln('\tfor (int $i = 0; $i < a.len; ++$i) {') - // compare every pair of elements of the two arrays - match elem_sym.kind { - .string { fn_builder.writeln('\t\tif (string_ne(*(($ptr_typ*)((byte*)a.data+($i*a.element_size))), *(($ptr_typ*)((byte*)b.data+($i*b.element_size))))) {') } - .struct_ { fn_builder.writeln('\t\tif (memcmp((byte*)a.data+($i*a.element_size), (byte*)b.data+($i*b.element_size), a.element_size)) {') } - .array { fn_builder.writeln('\t\tif (!${ptr_elem_typ}_arr_eq((($elem_typ*)a.data)[$i], (($elem_typ*)b.data)[$i])) {') } - .function { fn_builder.writeln('\t\tif (*((voidptr*)((byte*)a.data+($i*a.element_size))) != *((voidptr*)((byte*)b.data+($i*b.element_size)))) {') } - else { fn_builder.writeln('\t\tif (*(($ptr_typ*)((byte*)a.data+($i*a.element_size))) != *(($ptr_typ*)((byte*)b.data+($i*b.element_size)))) {') } - } - fn_builder.writeln('\t\t\treturn false;') - fn_builder.writeln('\t\t}') - fn_builder.writeln('\t}') - fn_builder.writeln('\treturn true;') - fn_builder.writeln('}') - g.auto_fn_definitions << fn_builder.str() - return ptr_typ -} - -fn (mut g Gen) gen_map_equality_fn(left table.Type) string { - left_sym := g.table.get_type_symbol(left) - ptr_typ := g.typ(left).trim('*') - value_sym := g.table.get_type_symbol(left_sym.map_info().value_type) - value_typ := g.typ(left_sym.map_info().value_type) - if value_sym.kind == .map { - // Recursively generate map element comparison function code if array element is map type - g.gen_map_equality_fn(left_sym.map_info().value_type) - } - if ptr_typ in g.map_fn_definitions { - return ptr_typ - } - g.map_fn_definitions << ptr_typ - g.type_definitions.writeln('static bool ${ptr_typ}_map_eq($ptr_typ a, $ptr_typ b); // auto') - mut fn_builder := strings.new_builder(512) - fn_builder.writeln('static bool ${ptr_typ}_map_eq($ptr_typ a, $ptr_typ b) {') - fn_builder.writeln('\tif (a.len != b.len) {') - fn_builder.writeln('\t\treturn false;') - fn_builder.writeln('\t}') - fn_builder.writeln('\tarray_string _keys = map_keys(&a);') - i := g.new_tmp_var() - fn_builder.writeln('\tfor (int $i = 0; $i < _keys.len; ++$i) {') - fn_builder.writeln('\t\tstring k = string_clone( ((string*)_keys.data)[$i]);') - if value_sym.kind == .function { - func := value_sym.info as table.FnType - ret_styp := g.typ(func.func.return_type) - fn_builder.write('\t\t$ret_styp (*v) (') - arg_len := func.func.params.len - for j, arg in func.func.params { - arg_styp := g.typ(arg.typ) - fn_builder.write('$arg_styp $arg.name') - if j < arg_len - 1 { - fn_builder.write(', ') - } - } - fn_builder.writeln(') = (*(voidptr*)map_get_1(&a, &k, &(voidptr[]){ 0 }));') - } else { - fn_builder.writeln('\t\t$value_typ v = (*($value_typ*)map_get_1(&a, &k, &($value_typ[]){ 0 }));') - } - match value_sym.kind { - .string { fn_builder.writeln('\t\tif (!map_exists(b, k) || string_ne((*(string*)map_get_1(&b, &k, &(string[]){_SLIT("")})), v)) {') } - .function { fn_builder.writeln('\t\tif (!map_exists(b, k) || (*(voidptr*)map_get_1(&b, &k, &(voidptr[]){ 0 })) != v) {') } - else { fn_builder.writeln('\t\tif (!map_exists(b, k) || (*($value_typ*)map_get_1(&b, &k, &($value_typ[]){ 0 })) != v) {') } - } - fn_builder.writeln('\t\t\treturn false;') - fn_builder.writeln('\t\t}') - fn_builder.writeln('\t}') - fn_builder.writeln('\treturn true;') - fn_builder.writeln('}') - g.auto_fn_definitions << fn_builder.str() - return ptr_typ -} - fn verror(s string) { util.verror('cgen error', s) } @@ -4932,416 +4794,6 @@ fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { return types_sorted } -// `nums.map(it % 2 == 0)` -fn (mut g Gen) gen_array_map(node ast.CallExpr) { - g.inside_lambda = true - tmp := g.new_tmp_var() - s := g.go_before_stmt(0) - // println('filter s="$s"') - ret_typ := g.typ(node.return_type) - // inp_typ := g.typ(node.receiver_type) - ret_sym := g.table.get_type_symbol(node.return_type) - inp_sym := g.table.get_type_symbol(node.receiver_type) - ret_info := ret_sym.info as table.Array - ret_elem_type := g.typ(ret_info.elem_type) - inp_info := inp_sym.info as table.Array - inp_elem_type := g.typ(inp_info.elem_type) - if inp_sym.kind != .array { - verror('map() requires an array') - } - g.write('${g.typ(node.left_type)} ${tmp}_orig = ') - g.expr(node.left) - g.writeln(';') - g.write('int ${tmp}_len = ${tmp}_orig.len;') - g.writeln('$ret_typ $tmp = __new_array(0, ${tmp}_len, sizeof($ret_elem_type));') - i := g.new_tmp_var() - g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') - g.write('\t$inp_elem_type it = (($inp_elem_type*) ${tmp}_orig.data)[$i];') - g.write('\t$ret_elem_type ti = ') - expr := node.args[0].expr - match expr { - ast.AnonFn { - g.gen_anon_fn_decl(expr) - g.write('${expr.decl.name}(it)') - } - ast.Ident { - if expr.kind == .function { - g.write('${c_name(expr.name)}(it)') - } else if expr.kind == .variable { - var_info := expr.var_info() - sym := g.table.get_type_symbol(var_info.typ) - if sym.kind == .function { - g.write('${c_name(expr.name)}(it)') - } else { - g.expr(node.args[0].expr) - } - } else { - g.expr(node.args[0].expr) - } - } - else { - g.expr(node.args[0].expr) - } - } - g.writeln(';') - g.writeln('\tarray_push(&$tmp, &ti);') - g.writeln('}') - g.write(s) - g.write(tmp) - g.inside_lambda = false -} - -// `users.sort(a.age < b.age)` -fn (mut g Gen) gen_array_sort(node ast.CallExpr) { - // println('filter s="$s"') - rec_sym := g.table.get_type_symbol(node.receiver_type) - if rec_sym.kind != .array { - println(node.name) - println(g.typ(node.receiver_type)) - // println(rec_sym.kind) - verror('.sort() is an array method') - } - info := rec_sym.info as table.Array - // No arguments means we are sorting an array of builtins (e.g. `numbers.sort()`) - // The type for the comparison fns is the type of the element itself. - mut typ := info.elem_type - mut is_reverse := false - // `users.sort(a.age > b.age)` - if node.args.len > 0 { - // Get the type of the field that's being compared - // `a.age > b.age` => `age int` => int - infix_expr := node.args[0].expr as ast.InfixExpr - // typ = infix_expr.left_type - is_reverse = infix_expr.op == .gt - } - mut compare_fn := '' - match typ { - table.int_type { - compare_fn = 'compare_ints' - } - table.u64_type { - compare_fn = 'compare_u64s' - } - table.string_type { - compare_fn = 'compare_strings' - } - table.f64_type { - compare_fn = 'compare_floats' - } - else { - // Generate a comparison function for a custom type - if node.args.len == 0 { - verror('usage: .sort(a.field < b.field)') - } - // verror('sort(): unhandled type $typ $q.name') - tmp_name := g.new_tmp_var() - compare_fn = 'compare_${tmp_name}_' + g.typ(typ) - if is_reverse { - compare_fn += '_reverse' - } - // Register a new custom `compare_xxx` function for qsort() - g.table.register_fn(name: compare_fn, return_type: table.int_type) - infix_expr := node.args[0].expr as ast.InfixExpr - styp := g.typ(typ) - // Variables `a` and `b` are used in the `.sort(a < b)` syntax, so we can reuse them - // when generating the function as long as the args are named the same. - g.definitions.writeln('int $compare_fn ($styp* a, $styp* b) {') - field_type := g.typ(infix_expr.left_type) - left_expr_str := g.write_expr_to_string(infix_expr.left).replace_once('.', - '->') - right_expr_str := g.write_expr_to_string(infix_expr.right).replace_once('.', - '->') - g.definitions.writeln('$field_type a_ = $left_expr_str;') - g.definitions.writeln('$field_type b_ = $right_expr_str;') - mut op1, mut op2 := '', '' - if infix_expr.left_type == table.string_type { - if is_reverse { - op1 = 'string_gt(a_, b_)' - op2 = 'string_lt(a_, b_)' - } else { - op1 = 'string_lt(a_, b_)' - op2 = 'string_gt(a_, b_)' - } - } else { - if is_reverse { - op1 = 'a_ > b_' - op2 = 'a_ < b_' - } else { - op1 = 'a_ < b_' - op2 = 'a_ > b_' - } - } - g.definitions.writeln('if ($op1) return -1;') - g.definitions.writeln('if ($op2) return 1; return 0; }\n') - } - } - if is_reverse && !compare_fn.ends_with('_reverse') { - compare_fn += '_reverse' - } - // - deref := if node.left_type.is_ptr() || node.left_type.is_pointer() { '->' } else { '.' } - // eprintln('> qsort: pointer $node.left_type | deref: `$deref`') - g.write('qsort(') - g.expr(node.left) - g.write('${deref}data, ') - g.expr(node.left) - g.write('${deref}len, ') - g.expr(node.left) - g.writeln('${deref}element_size, (int (*)(const void *, const void *))&$compare_fn);') -} - -// `nums.filter(it % 2 == 0)` -fn (mut g Gen) gen_array_filter(node ast.CallExpr) { - tmp := g.new_tmp_var() - s := g.go_before_stmt(0) - // println('filter s="$s"') - sym := g.table.get_type_symbol(node.return_type) - if sym.kind != .array { - verror('filter() requires an array') - } - info := sym.info as table.Array - styp := g.typ(node.return_type) - elem_type_str := g.typ(info.elem_type) - g.write('${g.typ(node.left_type)} ${tmp}_orig = ') - g.expr(node.left) - g.writeln(';') - g.write('int ${tmp}_len = ${tmp}_orig.len;') - g.writeln('$styp $tmp = __new_array(0, ${tmp}_len, sizeof($elem_type_str));') - i := g.new_tmp_var() - g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') - g.writeln(' $elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') - g.write('if (') - expr := node.args[0].expr - match expr { - ast.AnonFn { - g.gen_anon_fn_decl(expr) - g.write('${expr.decl.name}(it)') - } - ast.Ident { - if expr.kind == .function { - g.write('${c_name(expr.name)}(it)') - } else if expr.kind == .variable { - var_info := expr.var_info() - sym_t := g.table.get_type_symbol(var_info.typ) - if sym_t.kind == .function { - g.write('${c_name(expr.name)}(it)') - } else { - g.expr(node.args[0].expr) - } - } else { - g.expr(node.args[0].expr) - } - } - else { - g.expr(node.args[0].expr) - } - } - g.writeln(') array_push(&$tmp, &it); \n }') - g.write(s) - g.write(' ') - g.write(tmp) -} - -// `nums.insert(0, 2)` `nums.insert(0, [2,3,4])` -fn (mut g Gen) gen_array_insert(node ast.CallExpr) { - left_sym := g.table.get_type_symbol(node.left_type) - left_info := left_sym.info as table.Array - elem_type_str := g.typ(left_info.elem_type) - arg2_sym := g.table.get_type_symbol(node.args[1].typ) - is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type - if is_arg2_array { - g.write('array_insert_many(&') - } else { - g.write('array_insert(&') - } - g.expr(node.left) - g.write(', ') - g.expr(node.args[0].expr) - if is_arg2_array { - g.write(', ') - g.expr(node.args[1].expr) - g.write('.data, ') - g.expr(node.args[1].expr) - g.write('.len)') - } else { - g.write(', &($elem_type_str[]){') - if left_info.elem_type == table.string_type { - g.write('string_clone(') - } - g.expr(node.args[1].expr) - if left_info.elem_type == table.string_type { - g.write(')') - } - g.write('})') - } -} - -// `nums.prepend(2)` `nums.prepend([2,3,4])` -fn (mut g Gen) gen_array_prepend(node ast.CallExpr) { - left_sym := g.table.get_type_symbol(node.left_type) - left_info := left_sym.info as table.Array - elem_type_str := g.typ(left_info.elem_type) - arg_sym := g.table.get_type_symbol(node.args[0].typ) - is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type - if is_arg_array { - g.write('array_prepend_many(&') - } else { - g.write('array_prepend(&') - } - g.expr(node.left) - if is_arg_array { - g.write(', ') - g.expr(node.args[0].expr) - g.write('.data, ') - g.expr(node.args[0].expr) - g.write('.len)') - } else { - g.write(', &($elem_type_str[]){') - g.expr(node.args[0].expr) - g.write('})') - } -} - -fn (mut g Gen) gen_array_contains_method(left_type table.Type) string { - mut left_sym := g.table.get_type_symbol(left_type) - mut left_type_str := g.typ(left_type).replace('*', '') - left_info := left_sym.info as table.Array - mut elem_type_str := g.typ(left_info.elem_type) - elem_sym := g.table.get_type_symbol(left_info.elem_type) - if elem_sym.kind == .function { - left_type_str = 'array_voidptr' - elem_type_str = 'voidptr' - } - fn_name := '${left_type_str}_contains' - if !left_sym.has_method('contains') { - g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto') - mut fn_builder := strings.new_builder(512) - fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {') - fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {') - match elem_sym.kind { - .string { - fn_builder.writeln('\t\tif (string_eq((*(string*)array_get(a, i)), v)) {') - } - .array { - ptr_typ := g.gen_array_equality_fn(left_info.elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(*($elem_type_str*)array_get(a, i), v)) {') - } - .function { - fn_builder.writeln('\t\tif ((*(voidptr*)array_get(a, i)) == v) {') - } - .map { - ptr_typ := g.gen_map_equality_fn(left_info.elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(*($elem_type_str*)array_get(a, i), v)) {') - } - .struct_ { - ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(*($elem_type_str*)array_get(a, i), v)) {') - } - else { - fn_builder.writeln('\t\tif ((*($elem_type_str*)array_get(a, i)) == v) {') - } - } - fn_builder.writeln('\t\t\treturn true;') - fn_builder.writeln('\t\t}') - fn_builder.writeln('\t}') - fn_builder.writeln('\treturn false;') - fn_builder.writeln('}') - g.auto_fn_definitions << fn_builder.str() - left_sym.register_method(&table.Fn{ - name: 'contains' - params: [table.Param{ - typ: left_type - }, table.Param{ - typ: left_info.elem_type - }] - }) - } - return fn_name -} - -// `nums.contains(2)` -fn (mut g Gen) gen_array_contains(node ast.CallExpr) { - fn_name := g.gen_array_contains_method(node.left_type) - g.write('${fn_name}(') - if node.left_type.is_ptr() { - g.write('*') - } - g.expr(node.left) - g.write(', ') - g.expr(node.args[0].expr) - g.write(')') -} - -fn (mut g Gen) gen_array_index_method(left_type table.Type) string { - mut left_sym := g.table.get_type_symbol(left_type) - mut left_type_str := g.typ(left_type).replace('*', '') - left_info := left_sym.info as table.Array - mut elem_type_str := g.typ(left_info.elem_type) - elem_sym := g.table.get_type_symbol(left_info.elem_type) - if elem_sym.kind == .function { - left_type_str = 'array_voidptr' - elem_type_str = 'voidptr' - } - fn_name := '${left_type_str}_index' - if !left_sym.has_method('index') { - g.type_definitions.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v); // auto') - mut fn_builder := strings.new_builder(512) - fn_builder.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v) {') - fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {') - match elem_sym.kind { - .string { - fn_builder.writeln('\t\tif (string_eq((*(string*)array_get(a, i)), v)) {') - } - .array { - ptr_typ := g.gen_array_equality_fn(left_info.elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(*($elem_type_str*)array_get(a, i), v)) {') - } - .function { - fn_builder.writeln('\t\tif ((*(voidptr*)array_get(a, i)) == v) {') - } - .map { - ptr_typ := g.gen_map_equality_fn(left_info.elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(*($elem_type_str*)array_get(a, i), v)) {') - } - .struct_ { - ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(*($elem_type_str*)array_get(a, i), v)) {') - } - else { - fn_builder.writeln('\t\tif ((*($elem_type_str*)array_get(a, i)) == v) {') - } - } - fn_builder.writeln('\t\t\treturn i;') - fn_builder.writeln('\t\t}') - fn_builder.writeln('\t}') - fn_builder.writeln('\treturn -1;') - fn_builder.writeln('}') - g.auto_fn_definitions << fn_builder.str() - left_sym.register_method(&table.Fn{ - name: 'index' - params: [table.Param{ - typ: left_type - }, table.Param{ - typ: left_info.elem_type - }] - }) - } - return fn_name -} - -// `nums.index(2)` -fn (mut g Gen) gen_array_index(node ast.CallExpr) { - fn_name := g.gen_array_index_method(node.left_type) - g.write('${fn_name}(') - if node.left_type.is_ptr() { - g.write('*') - } - g.expr(node.left) - g.write(', ') - g.expr(node.args[0].expr) - g.write(')') -} - [inline] fn (g &Gen) nth_stmt_pos(n int) int { return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)] @@ -6096,120 +5548,6 @@ _Interface* I_${cctype}_to_Interface_${interface_name}_ptr($cctype* x) { return sb.str() } -fn (mut g Gen) array_init(it ast.ArrayInit) { - type_sym := g.table.get_type_symbol(it.typ) - styp := g.typ(it.typ) - mut shared_styp := '' // only needed for shared &[]{...} - is_amp := g.is_amp - g.is_amp = false - if is_amp { - g.out.go_back(1) // delete the `&` already generated in `prefix_expr() - if g.is_shared { - mut shared_typ := it.typ.set_flag(.shared_f) - shared_styp = g.typ(shared_typ) - g.writeln('($shared_styp*)memdup(&($shared_styp){.val = ') - } else { - g.write('($styp*)memdup(&') // TODO: doesn't work with every compiler - } - } else { - if g.is_shared { - g.writeln('{.val = ($styp*)') - } - } - if type_sym.kind == .array_fixed { - g.write('{') - if it.has_val { - for i, expr in it.exprs { - g.expr(expr) - if i != it.exprs.len - 1 { - g.write(', ') - } - } - } else { - g.write('0') - } - g.write('}') - return - } - elem_type_str := g.typ(it.elem_type) - if it.exprs.len == 0 { - elem_sym := g.table.get_type_symbol(it.elem_type) - is_default_array := elem_sym.kind == .array && it.has_default - if is_default_array { - g.write('__new_array_with_array_default(') - } else { - g.write('__new_array_with_default(') - } - if it.has_len { - g.expr(it.len_expr) - g.write(', ') - } else { - g.write('0, ') - } - if it.has_cap { - g.expr(it.cap_expr) - g.write(', ') - } else { - g.write('0, ') - } - if elem_sym.kind == .function { - g.write('sizeof(voidptr), ') - } else { - g.write('sizeof($elem_type_str), ') - } - if is_default_array { - g.write('($elem_type_str[]){') - g.expr(it.default_expr) - g.write('}[0])') - } else if it.has_default { - g.write('&($elem_type_str[]){') - g.expr(it.default_expr) - g.write('})') - } else if it.has_len && it.elem_type == table.string_type { - g.write('&($elem_type_str[]){') - g.write('_SLIT("")') - g.write('})') - } else { - g.write('0)') - } - return - } - len := it.exprs.len - elem_sym := g.table.get_type_symbol(it.elem_type) - if elem_sym.kind == .function { - g.write('new_array_from_c_array($len, $len, sizeof(voidptr), _MOV((voidptr[$len]){') - } else { - g.write('new_array_from_c_array($len, $len, sizeof($elem_type_str), _MOV(($elem_type_str[$len]){') - } - if len > 8 { - g.writeln('') - g.write('\t\t') - } - for i, expr in it.exprs { - if it.is_interface { - // sym := g.table.get_type_symbol(it.expr_types[i]) - // isym := g.table.get_type_symbol(it.interface_type) - g.interface_call(it.expr_types[i], it.interface_type) - } - g.expr_with_cast(expr, it.expr_types[i], it.elem_type) - if it.is_interface { - g.write(')') - } - if i != len - 1 { - g.write(', ') - } - } - g.write('}))') - if g.is_shared { - g.write(', .mtx = sync__new_rwmutex()}') - if is_amp { - g.write(', sizeof($shared_styp))') - } - } else if is_amp { - g.write(', sizeof($styp))') - } -} - // `ui.foo(button)` => // `ui__foo(I_ui__Button_to_ui__Widget(` ... fn (mut g Gen) interface_call(typ table.Type, interface_type table.Type) {