1220 lines
34 KiB
V
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('}')
|
|
}
|