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

1220 lines
34 KiB
V

module c
import strings
import v3.flat
import v3.types
fn gen_expr_lvalue(mut g FlatGen, id flat.NodeId) {
node := g.a.nodes[int(id)]
if node.kind == .index {
base_id := g.a.child(&node, 0)
base_type := g.tc.resolve_type(base_id)
if base_type is types.Map {
c_key := g.tc.c_type(base_type.key_type)
c_val := g.tc.c_type(base_type.value_type)
zero := if base_type.value_type is types.Array {
c_elem := g.tc.c_type(base_type.value_type.elem_type)
'&(${c_val}[]){array_new(sizeof(${c_elem}), 0, 0)}'
} else {
'&(${c_val}[]){0}'
}
g.write('(*(${c_val}*)map__get_or_set(&')
g.gen_expr(base_id)
g.write(', &(${c_key}[]){')
g.gen_expr(g.a.child(&node, 1))
g.write('}, ${zero}))')
return
}
}
g.gen_expr(id)
}
fn (mut g FlatGen) gen_node(id flat.NodeId) {
if int(id) < 0 {
return
}
node := g.a.nodes[int(id)]
match node.kind {
.fn_decl, .c_fn_decl {
return
}
.expr_stmt {
child_id := g.a.child(&node, 0)
child := g.a.nodes[int(child_id)]
if g.is_runtime_array_flags_stmt(child_id) {
return
}
if child.kind == .or_expr {
g.gen_or_expr_stmt(child)
return
} else if child.kind == .infix && child.op == .left_shift {
lhs_id := g.a.child(&child, 0)
if child.value == 'push_many' {
g.gen_array_push_many_stmt(lhs_id, g.a.child(&child, 1))
} else if child.value == 'push' {
push_rhs_id := g.a.child(&child, 1)
mut c_elem := if child.typ.len > 0 {
g.tc.c_type(g.tc.parse_type(child.typ))
} else {
'string'
}
lhs_arr_type := types.unwrap_pointer(g.tc.resolve_type(lhs_id))
if lhs_arr := array_like_type(lhs_arr_type) {
push_rhs_clean := types.unwrap_pointer(g.tc.resolve_type(push_rhs_id))
if rhs_arr := array_like_type(push_rhs_clean) {
if g.tc.c_type(lhs_arr.elem_type) !in ['array', 'Array']
&& g.tc.c_type(lhs_arr.elem_type) == g.tc.c_type(rhs_arr.elem_type) {
g.gen_array_push_many_stmt(lhs_id, push_rhs_id)
return
}
} else if rhs_fixed := array_fixed_type(push_rhs_clean) {
if g.tc.c_type(lhs_arr.elem_type) !in ['array', 'Array']
&& g.tc.c_type(lhs_arr.elem_type) == g.tc.c_type(rhs_fixed.elem_type) {
g.gen_array_push_many_stmt(lhs_id, push_rhs_id)
return
}
}
c_elem = g.tc.c_type(lhs_arr.elem_type)
}
lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
amp := if lhs_is_ptr { '' } else { '&' }
g.write('array_push(${amp}')
gen_expr_lvalue(mut g, lhs_id)
g.write(', &(${c_elem}[]){')
g.gen_expr(push_rhs_id)
g.writeln('});')
} else {
lhs_type := g.tc.resolve_type(lhs_id)
clean := types.unwrap_pointer(lhs_type)
if lhs_arr := array_like_type(clean) {
rhs_id := g.a.child(&child, 1)
rhs_type := g.tc.resolve_type(rhs_id)
rhs_clean := types.unwrap_pointer(rhs_type)
if rhs_arr := array_like_type(rhs_clean) {
if g.tc.c_type(lhs_arr.elem_type) !in ['array', 'Array']
&& g.tc.c_type(lhs_arr.elem_type) == g.tc.c_type(rhs_arr.elem_type) {
g.gen_array_push_many_stmt(lhs_id, rhs_id)
} else {
c_elem := g.tc.c_type(lhs_arr.elem_type)
lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
amp := if lhs_is_ptr { '' } else { '&' }
g.write('array_push(${amp}')
gen_expr_lvalue(mut g, lhs_id)
g.write(', &(${c_elem}[]){')
g.gen_expr(rhs_id)
g.writeln('});')
}
} else if rhs_fixed := array_fixed_type(rhs_clean) {
if g.tc.c_type(lhs_arr.elem_type) !in ['array', 'Array']
&& g.tc.c_type(lhs_arr.elem_type) == g.tc.c_type(rhs_fixed.elem_type) {
g.gen_array_push_many_stmt(lhs_id, rhs_id)
} else {
c_elem := g.tc.c_type(lhs_arr.elem_type)
lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
amp := if lhs_is_ptr { '' } else { '&' }
g.write('array_push(${amp}')
gen_expr_lvalue(mut g, lhs_id)
g.write(', &(${c_elem}[]){')
g.gen_expr(rhs_id)
g.writeln('});')
}
} else {
c_elem := g.tc.c_type(lhs_arr.elem_type)
lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
amp := if lhs_is_ptr { '' } else { '&' }
g.write('array_push(${amp}')
gen_expr_lvalue(mut g, lhs_id)
g.write(', &(${c_elem}[]){')
g.gen_expr(rhs_id)
g.writeln('});')
}
} else {
g.gen_expr(child_id)
g.writeln(';')
}
}
} else {
g.gen_expr(child_id)
g.writeln(';')
}
}
.decl_assign {
g.gen_decl_assign(node)
}
.assign, .selector_assign {
g.gen_assign(node)
}
.index_assign {
g.gen_index_assign(node)
}
.return_stmt {
if g.cur_fn_ret is types.Enum {
g.expected_enum = g.cur_fn_ret.name
}
if node.children_count > 0 && g.has_pending_defers() {
g.gen_return_with_defers(node)
g.expected_enum = ''
return
}
g.gen_all_defers()
if node.children_count > 0 {
ret_id := g.a.child(&node, 0)
ret_node := g.a.nodes[int(ret_id)]
if ret_node.kind == .call {
fn_n := g.a.child_node(&ret_node, 0)
if fn_n.value == 'error' || fn_n.value == 'error_with_code' {
if g.cur_fn_ret_is_optional {
ct := g.optional_type_name(g.cur_fn_ret)
g.write('return ')
g.gen_optional_error_from_call(ct, ret_node)
g.writeln(';')
} else {
g.write('return ')
g.gen_expr(ret_id)
g.writeln(';')
}
return
}
}
if g.cur_fn_ret_is_optional {
ct := g.optional_type_name(g.cur_fn_ret)
base := g.cur_fn_ret_base
if g.expr_is_optional_literal(ret_id, g.cur_fn_ret) {
g.write('return ')
g.gen_expr(ret_id)
g.writeln(';')
return
}
if base is types.MultiReturn && node.children_count > 1 {
base_ct := g.tc.c_type(base)
g.write('return (${ct}){.ok = true, .value = (${base_ct}){')
for i in 0 .. node.children_count {
if i > 0 {
g.write(', ')
}
child_id := g.a.child(&node, i)
if i < base.types.len {
g.gen_expr_with_expected_type(child_id, base.types[i])
} else {
g.gen_expr(child_id)
}
}
g.writeln('}};')
return
}
if ret_node.kind == .none_expr {
g.writeln('return (${ct}){.ok = false};')
return
}
if base is types.Void {
g.writeln('return (${ct}){.ok = false};')
} else {
raw_expr_type := g.tc.resolve_type(ret_id)
if g.optional_result_matches_base(raw_expr_type, base) {
g.write('return ')
g.gen_expr(ret_id)
g.writeln(';')
} else {
expr_type := g.usable_expr_type(ret_id)
mut expr_value_type := expr_type
if expr_type is types.OptionType {
expr_value_type = expr_type.base_type
} else if expr_type is types.ResultType {
expr_value_type = expr_type.base_type
}
base_ct := g.tc.c_type(base)
expr_ct := g.tc.c_type(expr_value_type)
struct_init_ct := if ret_node.kind == .struct_init {
g.struct_init_c_type_name(ret_node.value)
} else {
''
}
if expr_ct != base_ct && struct_init_ct != base_ct
&& !g.type_names_match(expr_value_type, base)
&& !g.type_can_wrap_as_sum(expr_value_type, base)
&& !g.types_numeric_compatible(expr_value_type, base)
&& !g.call_constructs_type(ret_id, base)
&& expr_value_type !is types.Primitive
&& expr_value_type !is types.Unknown {
g.writeln('return (${ct}){.ok = false};')
} else {
g.write('return (${ct}){.ok = true, .value = ')
g.gen_expr_with_expected_type(ret_id, base)
g.writeln('};')
}
}
}
} else if g.cur_fn_ret is types.MultiReturn {
if node.children_count > 1 {
ct := g.tc.c_type(g.cur_fn_ret)
g.write('return (${ct}){')
for i in 0 .. node.children_count {
if i > 0 {
g.write(', ')
}
g.gen_expr(g.a.child(&node, i))
}
g.writeln('};')
} else {
expr_type := g.usable_expr_type(ret_id)
if expr_type is types.MultiReturn {
g.write('return ')
g.gen_expr(ret_id)
g.writeln(';')
} else {
ct := g.tc.c_type(g.cur_fn_ret)
g.write('return (${ct}){')
g.gen_expr(ret_id)
g.writeln('};')
}
}
} else if ret_node.kind == .assoc {
g.gen_return_assoc(ret_node)
} else {
if g.cur_fn_ret is types.Interface {
ct := g.tc.c_type(g.cur_fn_ret)
g.writeln('return (${ct}){0};')
} else {
g.write('return ')
g.gen_expr(ret_id)
g.writeln(';')
}
}
} else {
if g.cur_fn_ret_is_optional {
ct := g.optional_type_name(g.cur_fn_ret)
g.writeln('return (${ct}){.ok = true};')
} else if g.cur_fn_name == 'main' {
g.writeln('return 0;')
} else {
g.writeln('return;')
}
}
g.expected_enum = ''
}
.defer_stmt {
if node.value == 'function' {
if count_name := g.fn_defer_counts[int(id)] {
g.writeln('${count_name}++;')
}
g.fn_defers << id
} else {
g.defers << g.a.child(&node, 0)
}
}
.for_stmt {
g.gen_for(node)
}
.for_in_stmt {
g.gen_for_in(node)
}
.break_stmt {
if node.value.len > 0 {
g.writeln('goto ${c_name(node.value)}_break;')
} else {
g.writeln('break;')
}
}
.continue_stmt {
if node.value.len > 0 {
g.writeln('goto ${c_name(node.value)}_continue;')
} else {
g.writeln('continue;')
}
}
.block {
g.writeln('{')
g.tc.push_scope()
defer_start := g.defers.len
g.indent++
for i in 0 .. node.children_count {
g.gen_node(g.a.child(&node, i))
}
g.gen_defers_from(defer_start)
g.trim_defers(defer_start)
g.indent--
g.tc.pop_scope()
g.writeln('}')
}
.if_expr {
g.gen_if(node)
}
.assert_stmt {
g.write('if (!(')
g.gen_expr(g.a.child(&node, 0))
g.writeln(')) {')
g.indent++
g.writeln('fprintf(stderr, "assert failed\\n");')
g.writeln('exit(1);')
g.indent--
g.writeln('}')
}
.goto_stmt {
g.writeln('goto ${c_name(node.value)};')
}
.label_stmt {
old_indent := g.indent
g.indent = 0
g.writeln('${c_name(node.value)}: ;')
g.indent = old_indent
}
.empty, .asm_stmt {}
else {
// NOTE: match_stmt is intentionally absent — the transformer lowers every
// match into an if/else-if chain (see transform.lower_match_stmts), so the
// backend never sees one. Match lowering lives in the transformer, not here.
eprintln('gen_node: unsupported node kind: ${node.kind}')
}
}
}
fn (g &FlatGen) has_pending_defers() bool {
return g.defers.len > 0 || g.fn_defers.len > 0
}
fn (mut g FlatGen) gen_return_with_defers(node flat.Node) {
ret_id := g.a.child(&node, 0)
ret_node := g.a.nodes[int(ret_id)]
if ret_node.kind == .assoc {
g.gen_return_assoc(ret_node)
return
}
ct := g.return_c_type()
expr := g.return_expr_string(node, ret_id, ret_node, ct)
tmp := g.tmp_name()
g.writeln('${ct} ${tmp} = ${expr};')
g.gen_all_defers()
g.writeln('return ${tmp};')
}
fn (mut g FlatGen) return_c_type() string {
if g.cur_fn_ret_is_optional {
return g.optional_type_name(g.cur_fn_ret)
}
return g.tc.c_type(g.cur_fn_ret)
}
fn (mut g FlatGen) return_expr_string(node flat.Node, ret_id flat.NodeId, ret_node flat.Node, ct string) string {
if ret_node.kind == .call {
fn_n := g.a.child_node(&ret_node, 0)
if fn_n.value == 'error' || fn_n.value == 'error_with_code' {
if g.cur_fn_ret_is_optional {
return g.optional_error_from_call_string(ct, ret_node)
}
return g.expr_to_string(ret_id)
}
}
if g.cur_fn_ret_is_optional {
base := g.cur_fn_ret_base
if g.expr_is_optional_literal(ret_id, g.cur_fn_ret) {
return g.expr_to_string(ret_id)
}
if base is types.MultiReturn && node.children_count > 1 {
base_ct := g.tc.c_type(base)
mut parts := []string{cap: int(node.children_count)}
for i in 0 .. node.children_count {
child_id := g.a.child(&node, i)
if i < base.types.len {
parts << g.expr_to_string_with_expected_type(child_id, base.types[i])
} else {
parts << g.expr_to_string(child_id)
}
}
return '(${ct}){.ok = true, .value = (${base_ct}){${parts.join(', ')}}}'
}
if ret_node.kind == .none_expr {
return '(${ct}){.ok = false}'
}
if base is types.Void {
return '(${ct}){.ok = false}'
}
raw_expr_type := g.tc.resolve_type(ret_id)
if g.optional_result_matches_base(raw_expr_type, base) {
return g.expr_to_string(ret_id)
}
expr_type := g.usable_expr_type(ret_id)
mut expr_value_type := expr_type
if expr_type is types.OptionType {
expr_value_type = expr_type.base_type
} else if expr_type is types.ResultType {
expr_value_type = expr_type.base_type
}
base_ct := g.tc.c_type(base)
expr_ct := g.tc.c_type(expr_value_type)
struct_init_ct := if ret_node.kind == .struct_init {
g.struct_init_c_type_name(ret_node.value)
} else {
''
}
if expr_ct != base_ct && struct_init_ct != base_ct
&& !g.type_names_match(expr_value_type, base)
&& !g.type_can_wrap_as_sum(expr_value_type, base)
&& !g.types_numeric_compatible(expr_value_type, base)
&& !g.call_constructs_type(ret_id, base) && expr_value_type !is types.Primitive
&& expr_value_type !is types.Unknown {
return '(${ct}){.ok = false}'
}
return '(${ct}){.ok = true, .value = ${g.expr_to_string_with_expected_type(ret_id, base)}}'
}
if g.cur_fn_ret is types.MultiReturn {
if node.children_count > 1 {
mut parts := []string{cap: int(node.children_count)}
for i in 0 .. node.children_count {
parts << g.expr_to_string(g.a.child(&node, i))
}
return '(${ct}){${parts.join(', ')}}'
}
expr_type := g.usable_expr_type(ret_id)
if expr_type is types.MultiReturn {
return g.expr_to_string(ret_id)
}
return '(${ct}){${g.expr_to_string(ret_id)}}'
}
if g.cur_fn_ret is types.Interface {
return '(${ct}){0}'
}
return g.expr_to_string(ret_id)
}
fn (mut g FlatGen) optional_error_from_call_string(ct string, node flat.Node) string {
orig := g.sb
orig_line_start := g.line_start
g.sb = strings.new_builder(64)
g.line_start = true
g.gen_optional_error_from_call(ct, node)
result := g.sb.str()
g.sb = orig
g.line_start = orig_line_start
return result
}
fn (g &FlatGen) expr_really_returns_optional(id flat.NodeId) bool {
if int(id) < 0 {
return false
}
node := g.a.nodes[int(id)]
if node.kind == .none_expr {
return true
}
if node.kind == .call {
if fname := g.tc.resolved_call_name(id) {
ret_type := g.tc.fn_ret_types[fname] or { return false }
return ret_type is types.OptionType || ret_type is types.ResultType
}
}
return false
}
fn (g &FlatGen) optional_result_matches_base(expr_type types.Type, base types.Type) bool {
if expr_type is types.OptionType {
return g.type_names_match(expr_type.base_type, base)
}
if expr_type is types.ResultType {
return g.type_names_match(expr_type.base_type, base)
}
return false
}
fn (g &FlatGen) usable_expr_type(id flat.NodeId) types.Type {
if typ := g.tc.expr_type(id) {
if typ !is types.Unknown && typ !is types.Void {
return typ
}
}
return g.tc.resolve_type(id)
}
fn (g &FlatGen) type_names_match(a types.Type, b types.Type) bool {
a_name := a.name()
b_name := b.name()
if a_name.len == 0 || b_name.len == 0 {
return false
}
if a_name == b_name {
return true
}
return a_name.all_after_last('.') == b_name.all_after_last('.')
}
fn (g &FlatGen) type_can_wrap_as_sum(actual types.Type, expected types.Type) bool {
expected0 := if expected is types.Alias { expected.base_type } else { expected }
if expected0 !is types.SumType {
return false
}
actual0 := if actual is types.Alias { actual.base_type } else { actual }
if actual0 is types.SumType {
return false
}
sum_type := expected0 as types.SumType
sum_name := g.resolve_sum_name(sum_type.name)
variant := g.resolve_variant(sum_name, actual0.name())
variants := g.tc.sum_types[sum_name] or { return false }
return variant in variants
}
fn (g &FlatGen) types_numeric_compatible(a types.Type, b types.Type) bool {
_ = g
return (a.is_integer() || a.is_float()) && (b.is_integer() || b.is_float())
}
fn (g &FlatGen) call_constructs_type(id flat.NodeId, target types.Type) bool {
if int(id) < 0 {
return false
}
node := g.a.nodes[int(id)]
if node.kind != .call || node.children_count == 0 {
return false
}
fn_node := g.a.child_node(&node, 0)
if fn_node.kind != .ident {
return false
}
target_name := target.name()
if target_name.len == 0 {
return false
}
short_target := target_name.all_after_last('.')
return fn_node.value == target_name || fn_node.value == short_target
}
fn (g &FlatGen) is_runtime_array_flags_stmt(id flat.NodeId) bool {
if int(id) < 0 {
return false
}
node := g.a.nodes[int(id)]
if node.kind != .call || node.children_count == 0 {
return false
}
fn_node := g.a.child_node(&node, 0)
if fn_node.kind != .selector || fn_node.value !in ['set', 'clear']
|| fn_node.children_count == 0 {
return false
}
flags_node := g.a.child_node(fn_node, 0)
if flags_node.kind != .selector || flags_node.value != 'flags' || flags_node.children_count == 0 {
return false
}
owner_id := g.a.child(flags_node, 0)
owner_type := types.unwrap_pointer(g.tc.resolve_type(owner_id))
return owner_type is types.Array || owner_type.name() == 'strings.Builder'
}
fn (mut g FlatGen) gen_decl_assign(node flat.Node) {
if node.children_count >= 3 {
rhs_type := g.tc.resolve_type(g.a.child(&node, 1))
if rhs_type is types.MultiReturn {
g.gen_multi_return_decl(node)
return
}
}
mut bad_decl_child := node.children_count % 2 == 1
for i in 0 .. node.children_count {
if int(g.a.child(&node, i)) < 0 {
bad_decl_child = true
}
}
if bad_decl_child {
mut parts := []string{}
for i in 0 .. node.children_count {
child_id := g.a.child(&node, i)
if int(child_id) < 0 {
parts << '${i}:empty'
} else {
child := g.a.nodes[int(child_id)]
parts << '${i}:${child.kind}:${child.value}:${child.typ}'
}
}
panic('internal error: odd decl_assign in ${g.cur_fn_name}: count=${node.children_count} typ=${node.typ} value=${node.value} children=${parts.join('|')}')
}
decl_prefix := if node.value == 'static' { 'static ' } else { '' }
mut i := 0
for i < node.children_count {
lhs_id := g.a.child(&node, i)
rhs_id := g.a.child(&node, i + 1)
lhs := g.a.nodes[int(lhs_id)]
rhs := g.a.nodes[int(rhs_id)]
lhs_is_defer_capture := lhs.kind == .ident && lhs.value in g.defer_capture_types
if rhs.kind == .array_literal {
elem_type := if rhs.children_count > 0 {
g.tc.resolve_type(g.a.child(&rhs, 0))
} else {
types.Type(types.int_)
}
if !lhs_is_defer_capture {
g.write('${decl_prefix}Array ')
}
g.gen_decl_lhs(lhs_id)
g.write(' = ')
g.gen_array_literal_value(rhs, elem_type)
g.writeln(';')
if lhs.kind == .ident {
g.tc.cur_scope.insert(lhs.value, types.Type(types.Array{
elem_type: elem_type
}))
}
} else if rhs.kind == .or_expr {
g.gen_decl_or_expr(lhs, rhs)
} else if rhs.kind == .array_init {
raw_init_type := g.tc.parse_type(rhs.value)
init_type := raw_init_type
if init_type is types.ArrayFixed {
c_elem := g.tc.c_type(init_type.elem_type)
lhs_str := g.decl_lhs_str(lhs_id)
len_expr := g.fixed_array_len_expr(rhs.value, init_type.len)
if len_expr == '0' {
if !lhs_is_defer_capture {
g.writeln('${decl_prefix}${c_elem} ${lhs_str}[0];')
}
} else {
if lhs_is_defer_capture {
g.writeln('memset(${lhs_str}, 0, sizeof(${lhs_str}));')
} else {
g.writeln('${decl_prefix}${c_elem} ${lhs_str}[${len_expr}] = {0};')
}
}
if lhs.kind == .ident {
g.tc.cur_scope.insert(lhs.value, raw_init_type)
}
} else {
c_elem := g.tc.c_type(init_type)
mut init_len := '0'
mut init_cap := '0'
mut init_val := ''
for j in 0 .. rhs.children_count {
child := g.a.child_node(&rhs, j)
if child.kind == .field_init {
if child.value == 'len' {
init_len = g.expr_to_string(g.a.child(child, 0))
} else if child.value == 'cap' {
init_cap = g.expr_to_string(g.a.child(child, 0))
} else if child.value == 'init' {
init_val = g.expr_to_string(g.a.child(child, 0))
}
}
}
lhs_str := g.decl_lhs_str(lhs_id)
if lhs_is_defer_capture {
g.writeln('${lhs_str} = array_new(sizeof(${c_elem}), ${init_len}, ${init_cap});')
} else {
g.writeln('${decl_prefix}Array ${lhs_str} = array_new(sizeof(${c_elem}), ${init_len}, ${init_cap});')
}
if init_val.len > 0 {
g.writeln('for (int _ai = 0; _ai < ${lhs_str}.len; _ai++) ((${c_elem}*)${lhs_str}.data)[_ai] = ${init_val};')
}
if lhs.kind == .ident {
g.tc.cur_scope.insert(lhs.value, types.Type(types.Array{
elem_type: init_type
}))
}
}
} else if rhs.kind == .map_init {
v_type := g.tc.resolve_type(rhs_id)
c_typ := g.tc.c_type(v_type)
if !lhs_is_defer_capture {
g.write('${decl_prefix}${c_typ} ')
}
g.gen_decl_lhs(lhs_id)
g.write(' = ')
g.gen_expr(rhs_id)
g.writeln(';')
if lhs.kind == .ident {
g.tc.cur_scope.insert(lhs.value, v_type)
}
if rhs.children_count > 0 {
if v_type is types.Map {
c_key := g.tc.c_type(v_type.key_type)
c_val := g.tc.c_type(v_type.value_type)
for j := 0; j < rhs.children_count; j += 2 {
g.write('map__set(&')
g.gen_decl_lhs(lhs_id)
g.write(', &(${c_key}[]){')
g.gen_expr(g.a.child(&rhs, j))
g.write('}, &(${c_val}[]){')
g.gen_expr(g.a.child(&rhs, j + 1))
g.writeln('});')
}
}
}
} else {
v_type := if node.typ.len > 0 {
g.tc.parse_type(node.typ)
} else if rhs.kind == .if_expr {
g.if_expr_type(&rhs)
} else {
g.tc.resolve_type(rhs_id)
}
ct0 := g.tc.c_type(v_type)
ct := if v_type is types.OptionType || v_type is types.ResultType {
g.optional_type_name(v_type)
} else {
ct0
}
if ct.starts_with('fn_ptr:') {
fp_name := g.resolve_fn_ptr_type(ct)
if !lhs_is_defer_capture {
g.write('${decl_prefix}${fp_name} ')
}
} else {
if !lhs_is_defer_capture {
g.write('${decl_prefix}${ct} ')
}
}
g.gen_decl_lhs(lhs_id)
g.write(' = ')
g.gen_expr(rhs_id)
g.writeln(';')
if lhs.kind == .ident {
g.tc.cur_scope.insert(lhs.value, v_type)
}
}
i += 2
}
}
fn (mut g FlatGen) gen_multi_return_decl(node flat.Node) {
rhs_id := g.a.child(&node, 1)
rhs_type := g.tc.resolve_type(rhs_id)
ct := g.tc.c_type(rhs_type)
tmp := g.tmp_name()
g.write('${ct} ${tmp} = ')
g.gen_expr(rhs_id)
g.writeln(';')
num_lhs := node.children_count - 1
mut multi_types := []types.Type{}
if rhs_type is types.MultiReturn {
multi_types = rhs_type.types.clone()
}
for j in 0 .. num_lhs {
lhs_idx := if j == 0 { 0 } else { j + 1 }
lhs_id := g.a.child(&node, lhs_idx)
lhs := g.a.nodes[int(lhs_id)]
if lhs.kind == .ident && lhs.value == '_' {
continue
}
field_type := if j < multi_types.len {
g.tc.c_type(multi_types[j])
} else {
'int'
}
lhs_name := c_name(lhs.value)
g.writeln('${field_type} ${lhs_name} = ${tmp}.arg${j};')
if lhs.kind == .ident {
inner := if j < multi_types.len {
multi_types[j]
} else {
types.Type(types.int_)
}
g.tc.cur_scope.insert(lhs.value, inner)
}
}
}
fn (mut g FlatGen) gen_assign(node flat.Node) {
if node.children_count >= 3 {
rhs_id := g.a.child(&node, 1)
rhs_type := g.tc.resolve_type(rhs_id)
if rhs_type is types.MultiReturn {
g.gen_multi_return_assign(node)
return
}
}
mut i := 0
for i < node.children_count {
lhs := g.a.nodes[int(g.a.child(&node, i))]
if lhs.kind == .ident && lhs.value == '_' {
g.write('(void)(')
g.gen_expr(g.a.child(&node, i + 1))
g.writeln(');')
} else if node.op == .left_shift_assign && lhs.kind == .ident {
if node.value == 'push_many' {
g.gen_array_push_many_stmt(g.a.child(&node, i), g.a.child(&node, i + 1))
} else if node.value == 'push' {
lhs_id := g.a.child(&node, i)
rhs_id := g.a.child(&node, i + 1)
lhs_arr_type := types.unwrap_pointer(g.tc.resolve_type(lhs_id))
if lhs_arr := array_like_type(lhs_arr_type) {
push_rhs_clean := types.unwrap_pointer(g.tc.resolve_type(rhs_id))
if rhs_arr := array_like_type(push_rhs_clean) {
if g.tc.c_type(lhs_arr.elem_type) !in ['array', 'Array']
&& g.tc.c_type(lhs_arr.elem_type) == g.tc.c_type(rhs_arr.elem_type) {
g.gen_array_push_many_stmt(lhs_id, rhs_id)
i += 2
continue
}
} else if rhs_fixed := array_fixed_type(push_rhs_clean) {
if g.tc.c_type(lhs_arr.elem_type) !in ['array', 'Array']
&& g.tc.c_type(lhs_arr.elem_type) == g.tc.c_type(rhs_fixed.elem_type) {
g.gen_array_push_many_stmt(lhs_id, rhs_id)
i += 2
continue
}
}
}
lhs_is_ptr := g.tc.resolve_type(g.a.child(&node, i)) is types.Pointer
amp := if lhs_is_ptr { '' } else { '&' }
c_elem := g.tc.c_type(g.tc.parse_type(node.typ))
g.write('array_push(${amp}${c_name(lhs.value)}, &(${c_elem}[]){')
g.gen_expr(g.a.child(&node, i + 1))
g.writeln('});')
} else {
// Array appends are annotated by the transformer; an un-annotated
// `<<=` here is the integer bit-shift-assign operator.
g.gen_expr(g.a.child(&node, i))
g.write(' <<= ')
g.gen_expr(g.a.child(&node, i + 1))
g.writeln(';')
}
} else {
rhs_id := g.a.child(&node, i + 1)
rhs_node := g.a.nodes[int(rhs_id)]
if rhs_node.kind == .or_expr {
g.gen_assign_or_expr(node, i, rhs_node)
i += 2
continue
}
lhs_id := g.a.child(&node, i)
if rhs_node.kind == .array_literal {
lhs_type := types.unwrap_pointer(g.tc.resolve_type(lhs_id))
elem_type := if rhs_node.children_count > 0 {
g.tc.resolve_type(g.a.child(&rhs_node, 0))
} else if lhs_arr := array_like_type(lhs_type) {
lhs_arr.elem_type
} else {
types.Type(types.int_)
}
g.gen_expr(g.a.child(&node, i))
g.write(' = ')
g.gen_array_literal_value(rhs_node, elem_type)
g.writeln(';')
} else {
lhs_type := g.tc.resolve_type(lhs_id)
rhs_type := g.tc.resolve_type(rhs_id)
if node.op == .plus_assign && (lhs_type is types.String || rhs_type is types.String) {
g.gen_expr(g.a.child(&node, i))
g.write(' = string__plus(')
g.gen_expr(g.a.child(&node, i))
g.write(', ')
g.gen_expr(rhs_id)
g.writeln(');')
i += 2
continue
}
if lhs_type is types.Enum {
g.expected_enum = lhs_type.name
}
if g.assign_lhs_needs_deref(g.a.child(&node, i), lhs_type, rhs_type, node.op) {
g.write('*')
}
g.gen_expr(g.a.child(&node, i))
g.write(' ${g.op_str(node.op)} ')
g.gen_expr(rhs_id)
g.writeln(';')
g.expected_enum = ''
}
}
i += 2
}
}
fn (g &FlatGen) assign_lhs_needs_deref(lhs_id flat.NodeId, lhs_type types.Type, rhs_type types.Type, op flat.Op) bool {
if op != .assign {
return false
}
lhs := g.a.nodes[int(lhs_id)]
if lhs.kind != .ident {
return false
}
if lhs_type is types.Pointer {
return lhs_type.base_type.name() == rhs_type.name()
}
return false
}
fn (mut g FlatGen) gen_multi_return_assign(node flat.Node) {
rhs_id := g.a.child(&node, 1)
rhs_type := g.tc.resolve_type(rhs_id)
ct := g.tc.c_type(rhs_type)
tmp := g.tmp_name()
g.write('${ct} ${tmp} = ')
g.gen_expr(rhs_id)
g.writeln(';')
num_lhs := node.children_count - 1
for j in 0 .. num_lhs {
lhs_idx := if j == 0 { 0 } else { j + 1 }
lhs_id := g.a.child(&node, lhs_idx)
lhs := g.a.nodes[int(lhs_id)]
if lhs.kind == .ident && lhs.value == '_' {
continue
}
gen_expr_lvalue(mut g, lhs_id)
g.writeln(' = ${tmp}.arg${j};')
}
}
fn (mut g FlatGen) gen_decl_lhs(id flat.NodeId) {
node := g.a.nodes[int(id)]
if node.kind == .ident {
g.write(c_name(node.value))
} else {
g.gen_expr(id)
}
}
fn (mut g FlatGen) decl_lhs_str(id flat.NodeId) string {
node := g.a.nodes[int(id)]
if node.kind == .ident {
return c_name(node.value)
}
return g.expr_to_string(id)
}
fn (mut g FlatGen) gen_assign_or_expr(node flat.Node, lhs_idx int, or_node flat.Node) {
expr_id := g.a.child(&or_node, 0)
or_body_id := g.a.child(&or_node, 1)
or_body := g.a.nodes[int(or_body_id)]
tmp := g.tmp_name()
expr_type := g.tc.resolve_type(expr_id)
opt_ct := g.optional_type_name(expr_type)
g.write('${opt_ct} ${tmp} = ')
g.gen_expr(expr_id)
g.writeln(';')
g.writeln('if (${tmp}.ok) {')
g.indent++
g.gen_expr(g.a.child(&node, lhs_idx))
g.writeln(' = ${tmp}.value;')
g.indent--
g.writeln('} else {')
g.tc.push_scope()
g.tc.cur_scope.insert('err', types.Type(types.Struct{
name: 'IError'
}))
g.indent++
g.writeln('IError err = ${tmp}.err;')
if or_node.value == '!' || or_node.value == '?' {
if g.cur_fn_ret_is_optional {
fn_opt_ct := g.optional_type_name(g.cur_fn_ret)
g.writeln('return (${fn_opt_ct}){.ok = false, .err = err};')
} else {
g.writeln('v_panic(IError__str(err));')
}
} else {
for j in 0 .. or_body.children_count {
child_id := g.a.child(&or_body, j)
g.gen_node(child_id)
}
}
g.indent--
g.tc.pop_scope()
g.writeln('}')
}
fn (mut g FlatGen) gen_decl_or_expr(lhs flat.Node, or_node flat.Node) {
expr_id := g.a.child(&or_node, 0)
or_body_id := g.a.child(&or_node, 1)
or_body := g.a.nodes[int(or_body_id)]
expr_node := g.a.nodes[int(expr_id)]
if expr_node.kind == .index {
base_type := g.tc.resolve_type(g.a.child(&expr_node, 0))
clean := types.unwrap_pointer(base_type)
if clean is types.Map {
g.gen_decl_or_map_index(lhs, expr_node, clean, or_body)
return
}
}
tmp := g.tmp_name()
expr_type := g.tc.resolve_type(expr_id)
opt_ct := g.optional_type_name(expr_type)
val_ct, val_type := g.optional_value_ct(expr_type)
g.tc.cur_scope.insert(lhs.value, val_type)
g.write('${opt_ct} ${tmp} = ')
g.gen_expr(expr_id)
g.writeln(';')
g.writeln('${val_ct} ${c_name(lhs.value)};')
g.writeln('if (${tmp}.ok) {')
g.indent++
g.writeln('${c_name(lhs.value)} = ${tmp}.value;')
g.indent--
g.writeln('} else {')
g.tc.push_scope()
g.tc.cur_scope.insert('err', types.Type(types.Struct{
name: 'IError'
}))
g.indent++
g.writeln('IError err = ${tmp}.err;')
if or_node.value == '!' || or_node.value == '?' {
if g.cur_fn_ret_is_optional {
fn_opt_ct := g.optional_type_name(g.cur_fn_ret)
g.writeln('return (${fn_opt_ct}){.ok = false, .err = err};')
} else {
g.writeln('v_panic(IError__str(err));')
}
} else if or_body.children_count > 0 {
for i in 0 .. or_body.children_count {
child_id := g.a.child(&or_body, i)
child := g.a.nodes[int(child_id)]
if i == or_body.children_count - 1 && child.kind == .expr_stmt {
inner := g.a.child_node(&child, 0)
if inner.kind == .call && g.is_noreturn_call(inner) {
g.gen_node(child_id)
} else {
g.write('${c_name(lhs.value)} = ')
g.gen_expr(g.a.child(&child, 0))
g.writeln(';')
}
} else {
g.gen_node(child_id)
}
}
}
g.indent--
g.tc.pop_scope()
g.writeln('}')
}
fn (mut g FlatGen) gen_decl_or_map_index(lhs flat.Node, expr_node flat.Node, m types.Map, or_body flat.Node) {
tmp := g.tmp_name()
c_val := g.tc.c_type(m.value_type)
c_key := g.tc.c_type(m.key_type)
g.tc.cur_scope.insert(lhs.value, m.value_type)
g.write('void* ${tmp} = map__get_check(&')
g.gen_expr(g.a.child(&expr_node, 0))
g.write(', &(${c_key}[]){')
g.gen_expr(g.a.child(&expr_node, 1))
g.writeln('});')
g.writeln('${c_val} ${c_name(lhs.value)};')
g.writeln('if (${tmp}) {')
g.indent++
g.writeln('${c_name(lhs.value)} = *(${c_val}*)${tmp};')
g.indent--
g.writeln('} else {')
g.indent++
if or_body.children_count > 0 {
for i in 0 .. or_body.children_count {
child_id := g.a.child(&or_body, i)
child := g.a.nodes[int(child_id)]
if i == or_body.children_count - 1 && child.kind == .expr_stmt {
inner := g.a.child_node(&child, 0)
if inner.kind == .call && g.is_noreturn_call(inner) {
g.gen_node(child_id)
} else {
g.write('${c_name(lhs.value)} = ')
g.gen_expr(g.a.child(&child, 0))
g.writeln(';')
}
} else {
g.gen_node(child_id)
}
}
}
g.indent--
g.writeln('}')
}
fn (g &FlatGen) is_noreturn_call(node &flat.Node) bool {
if node.kind != .call || node.children_count == 0 {
return false
}
fn_node := g.a.child_node(node, 0)
return fn_node.value in ['panic', 'exit']
}
fn (mut g FlatGen) tmp_name() string {
g.tmp_count++
return '_t${g.tmp_count}'
}
fn (mut g FlatGen) gen_or_expr(node flat.Node) {
expr_id := g.a.child(&node, 0)
or_body_id := g.a.child(&node, 1)
or_body := g.a.nodes[int(or_body_id)]
expr_node := g.a.nodes[int(expr_id)]
if expr_node.kind == .index {
base_type := g.tc.resolve_type(g.a.child(&expr_node, 0))
clean := types.unwrap_pointer(base_type)
if clean is types.Map {
g.gen_or_map_index(expr_node, clean, or_body)
return
}
}
tmp := g.tmp_name()
expr_type := g.tc.resolve_type(expr_id)
opt_ct := g.optional_type_name(expr_type)
g.write('({${opt_ct} ${tmp} = ')
g.gen_expr(expr_id)
g.write('; ${tmp}.ok ? ${tmp}.value : ({IError err = ${tmp}.err; (void)err; ')
g.gen_or_body(or_body)
g.write(';});})')
}
fn (mut g FlatGen) gen_or_body(or_body flat.Node) {
if or_body.children_count == 1 {
last_id := g.a.child(&or_body, or_body.children_count - 1)
last := g.a.nodes[int(last_id)]
if last.kind == .expr_stmt {
g.gen_expr(g.a.child(&last, 0))
} else {
g.gen_expr(last_id)
}
} else {
g.write('({')
for i in 0 .. or_body.children_count {
child_id := g.a.child(&or_body, i)
child := g.a.nodes[int(child_id)]
if i == or_body.children_count - 1 && child.kind == .expr_stmt {
g.gen_expr(g.a.child(&child, 0))
g.write(';')
} else {
g.gen_node(child_id)
}
}
g.write('})')
}
}
fn (mut g FlatGen) gen_or_map_index(expr_node flat.Node, m types.Map, or_body flat.Node) {
tmp := g.tmp_name()
c_val := g.tc.c_type(m.value_type)
c_key := g.tc.c_type(m.key_type)
g.write('({void* ${tmp} = map__get_check(&')
g.gen_expr(g.a.child(&expr_node, 0))
g.write(', &(${c_key}[]){')
g.gen_expr(g.a.child(&expr_node, 1))
g.write('}); ${tmp} ? *(${c_val}*)${tmp} : ')
g.gen_or_body(or_body)
g.write(';})')
}
fn (mut g FlatGen) gen_or_expr_stmt(node flat.Node) {
expr_id := g.a.child(&node, 0)
or_body_id := g.a.child(&node, 1)
or_body := g.a.nodes[int(or_body_id)]
tmp := g.tmp_name()
expr_type := g.tc.resolve_type(expr_id)
opt_ct := g.optional_type_name(expr_type)
g.writeln('${opt_ct} ${tmp} = ')
g.gen_expr(expr_id)
g.writeln(';')
g.writeln('if (!${tmp}.ok) {')
g.tc.push_scope()
g.tc.cur_scope.insert('err', types.Type(types.Struct{
name: 'IError'
}))
g.indent++
g.writeln('IError err = ${tmp}.err;')
if node.value == '!' || node.value == '?' {
if g.cur_fn_ret_is_optional {
fn_opt_ct := g.optional_type_name(g.cur_fn_ret)
g.writeln('return (${fn_opt_ct}){.ok = false, .err = err};')
} else {
g.writeln('v_panic(IError__str(err));')
}
} else {
for i in 0 .. or_body.children_count {
g.gen_node(g.a.child(&or_body, i))
}
}
g.indent--
g.tc.pop_scope()
g.writeln('}')
}