Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,392 @@
|
||||
module c
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
fn array_like_type(t types.Type) ?types.Array {
|
||||
if t is types.Array {
|
||||
return t
|
||||
}
|
||||
if t is types.Alias {
|
||||
base := t.base_type
|
||||
if base is types.Array {
|
||||
return base
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn array_fixed_type(t types.Type) ?types.ArrayFixed {
|
||||
if t is types.ArrayFixed {
|
||||
return t
|
||||
}
|
||||
if t is types.Alias {
|
||||
base := t.base_type
|
||||
if base is types.ArrayFixed {
|
||||
return base
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_array_literal_value(node flat.Node, elem_type types.Type) {
|
||||
c_elem := g.tc.c_type(elem_type)
|
||||
count := node.children_count
|
||||
if count == 0 {
|
||||
g.write('array_new(sizeof(${c_elem}), 0, 0)')
|
||||
return
|
||||
}
|
||||
g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
|
||||
for i in 0 .. count {
|
||||
if i > 0 {
|
||||
g.write(', ')
|
||||
}
|
||||
g.gen_expr(g.a.child(&node, i))
|
||||
}
|
||||
g.write('})')
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_fixed_array_data_arg(id flat.NodeId, arr types.ArrayFixed) {
|
||||
node := g.a.nodes[int(id)]
|
||||
if node.kind == .array_literal {
|
||||
c_elem := g.tc.c_type(arr.elem_type)
|
||||
g.write('(${c_elem}[]){')
|
||||
for i in 0 .. node.children_count {
|
||||
if i > 0 {
|
||||
g.write(', ')
|
||||
}
|
||||
g.gen_expr(g.a.child(&node, i))
|
||||
}
|
||||
g.write('}')
|
||||
return
|
||||
}
|
||||
g.gen_expr(id)
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_array_push_many_stmt(lhs_id flat.NodeId, rhs_id flat.NodeId) {
|
||||
lhs_is_ptr := g.tc.resolve_type(lhs_id) is types.Pointer
|
||||
amp := if lhs_is_ptr { '' } else { '&' }
|
||||
rhs_type := types.unwrap_pointer(g.tc.resolve_type(rhs_id))
|
||||
if rhs_fixed := array_fixed_type(rhs_type) {
|
||||
g.write('array_push_many_ptr(${amp}')
|
||||
gen_expr_lvalue(mut g, lhs_id)
|
||||
g.write(', ')
|
||||
g.gen_fixed_array_data_arg(rhs_id, rhs_fixed)
|
||||
len_expr := g.fixed_array_len_value(rhs_fixed)
|
||||
g.writeln(', ${len_expr});')
|
||||
return
|
||||
}
|
||||
g.write('array_push_many(${amp}')
|
||||
gen_expr_lvalue(mut g, lhs_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(rhs_id)
|
||||
g.writeln(');')
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_slice_expr(node flat.Node, base_id flat.NodeId, base_type types.Type) {
|
||||
start_node := g.a.child_node(&node, 1)
|
||||
has_start := start_node.kind != .empty
|
||||
has_end := node.children_count > 2
|
||||
base_str := g.expr_to_string(base_id)
|
||||
start_str := if has_start { g.expr_to_string(g.a.child(&node, 1)) } else { '0' }
|
||||
end_str := if has_end {
|
||||
g.expr_to_string(g.a.child(&node, 2))
|
||||
} else {
|
||||
'${base_str}.len'
|
||||
}
|
||||
if base_type is types.String {
|
||||
g.write('string__substr(${base_str}, ${start_str}, ${end_str})')
|
||||
} else if base_type is types.Array {
|
||||
g.write('array_slice(${base_str}, ${start_str}, ${end_str})')
|
||||
} else {
|
||||
g.write('string__substr(${base_str}, ${start_str}, ${end_str})')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr types.Array) {
|
||||
c_elem := g.tc.c_type(arr.elem_type)
|
||||
base_id := g.a.child(fn_node, 0)
|
||||
base_node := g.a.nodes[int(base_id)]
|
||||
is_ptr := if base_node.kind == .ident {
|
||||
g.tc.resolve_type(base_id) is types.Pointer
|
||||
} else {
|
||||
false
|
||||
}
|
||||
dot := if is_ptr { '->' } else { '.' }
|
||||
match fn_node.value {
|
||||
'clone' {
|
||||
g.write('array_clone(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(')')
|
||||
}
|
||||
'last' {
|
||||
g.write('*(${c_elem}*)array_get(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(base_id)
|
||||
g.write('.len - 1)')
|
||||
}
|
||||
'first' {
|
||||
g.write('*(${c_elem}*)array_get(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', 0)')
|
||||
}
|
||||
'delete_last' {
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}len--')
|
||||
}
|
||||
'pop' {
|
||||
g.write('({ ${c_elem} _pop${g.tmp_count} = *(${c_elem}*)array_get(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}len - 1); ')
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}len--; _pop${g.tmp_count}; })')
|
||||
g.tmp_count++
|
||||
}
|
||||
'clear' {
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}len = 0')
|
||||
}
|
||||
'push_many' {
|
||||
amp := if is_ptr { '' } else { '&' }
|
||||
g.write('array_push_many_ptr(${amp}')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 2))
|
||||
g.write(')')
|
||||
}
|
||||
'repeat' {
|
||||
g.write('array__repeat_to_depth(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(', 0)')
|
||||
}
|
||||
'repeat_to_depth' {
|
||||
g.write('array__repeat_to_depth(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 2))
|
||||
g.write(')')
|
||||
}
|
||||
'trim' {
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}len = ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
}
|
||||
'ensure_cap' {
|
||||
amp := if is_ptr { '' } else { '&' }
|
||||
g.write('array_ensure_cap(${amp}')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(')')
|
||||
}
|
||||
'delete' {
|
||||
amp := if is_ptr { '' } else { '&' }
|
||||
g.write('array_delete(${amp}')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(')')
|
||||
}
|
||||
'prepend' {
|
||||
amp := if is_ptr { '' } else { '&' }
|
||||
g.write('array__prepend(${amp}')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', &(${c_elem}[]){')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write('})')
|
||||
}
|
||||
'free' {
|
||||
g.write('array__free(')
|
||||
if !is_ptr {
|
||||
g.write('&')
|
||||
}
|
||||
g.gen_expr(base_id)
|
||||
g.write(')')
|
||||
}
|
||||
'str' {
|
||||
amp := if is_ptr { '' } else { '&' }
|
||||
g.write('strings__Builder__str(${amp}')
|
||||
g.gen_expr(base_id)
|
||||
g.write(')')
|
||||
}
|
||||
'join' {
|
||||
g.write('array_string_join(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(')')
|
||||
}
|
||||
'bytestr' {
|
||||
g.write('u8__vstring_with_len((u8*)')
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}data, ')
|
||||
g.gen_expr(base_id)
|
||||
g.write('${dot}len)')
|
||||
}
|
||||
'contains' {
|
||||
contains_fn := 'array_contains_${array_lookup_suffix(arr.elem_type)}'
|
||||
g.write('${contains_fn}(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(')')
|
||||
}
|
||||
'index' {
|
||||
index_fn := 'array_index_${array_lookup_suffix(arr.elem_type)}'
|
||||
g.write('${index_fn}(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write(')')
|
||||
}
|
||||
else {
|
||||
best_mname := g.array_method_fallback(fn_node.value)
|
||||
if best_mname.len > 0 {
|
||||
g.write(c_name(best_mname))
|
||||
g.write('(')
|
||||
ptypes := g.tc.fn_param_types[best_mname]
|
||||
wants_ptr := ptypes.len > 0 && ptypes[0] is types.Pointer
|
||||
if wants_ptr && !is_ptr {
|
||||
g.write('&')
|
||||
} else if !wants_ptr && is_ptr {
|
||||
g.write('*')
|
||||
}
|
||||
g.gen_expr(base_id)
|
||||
for i in 1 .. node.children_count {
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&node, i))
|
||||
}
|
||||
g.write(')')
|
||||
} else {
|
||||
g.gen_expr(g.a.child(&node, 0))
|
||||
g.write('(')
|
||||
g.gen_expr(base_id)
|
||||
g.write(')')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn array_lookup_suffix(elem_type types.Type) string {
|
||||
if elem_type is types.String {
|
||||
return 'string'
|
||||
}
|
||||
if elem_type is types.Primitive {
|
||||
if elem_type.props.has(.unsigned) && elem_type.size == 8 {
|
||||
return 'u8'
|
||||
}
|
||||
}
|
||||
return 'int'
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) array_method_fallback(method string) string {
|
||||
if method in g.array_method_cache {
|
||||
return g.array_method_cache[method]
|
||||
}
|
||||
suffix := '.${method}'
|
||||
mut best_mname := ''
|
||||
for mname, _ in g.tc.fn_param_types {
|
||||
if mname.ends_with(suffix) {
|
||||
if best_mname.len == 0 || mname.len > best_mname.len {
|
||||
best_mname = mname
|
||||
}
|
||||
}
|
||||
}
|
||||
g.array_method_cache[method] = best_mname
|
||||
return best_mname
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_map_ref_arg(base_id flat.NodeId, base_type types.Type) {
|
||||
if base_type is types.Pointer {
|
||||
g.gen_expr(base_id)
|
||||
} else {
|
||||
g.write('&')
|
||||
g.gen_expr(base_id)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_map_delete(node flat.Node, fn_node &flat.Node, m types.Map, base_type types.Type) {
|
||||
c_key := g.tc.c_type(m.key_type)
|
||||
g.write('map__delete(')
|
||||
g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
|
||||
g.write(', &(${c_key}[]){')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.write('})')
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_index_assign(node flat.Node) {
|
||||
lhs_id := g.a.child(&node, 0)
|
||||
lhs := g.a.nodes[int(lhs_id)]
|
||||
if lhs.kind == .index {
|
||||
base_id := g.a.child(&lhs, 0)
|
||||
base_type := g.tc.resolve_type(base_id)
|
||||
clean_base := types.unwrap_pointer(base_type)
|
||||
if clean_base is types.Map {
|
||||
c_key := g.tc.c_type(clean_base.key_type)
|
||||
c_val := g.tc.c_type(clean_base.value_type)
|
||||
is_ptr := base_type is types.Pointer
|
||||
if is_ptr {
|
||||
g.write('map__set(')
|
||||
} else {
|
||||
g.write('map__set(&')
|
||||
}
|
||||
g.gen_expr(base_id)
|
||||
g.write(', &(${c_key}[]){')
|
||||
g.gen_expr_with_expected_type(g.a.child(&lhs, 1), clean_base.key_type)
|
||||
g.write('}, &(${c_val}[]){')
|
||||
g.gen_expr_with_expected_type(g.a.child(&node, 1), clean_base.value_type)
|
||||
g.writeln('});')
|
||||
return
|
||||
}
|
||||
if base_type is types.Pointer {
|
||||
ptr_type := base_type
|
||||
if ptr_type.base_type is types.Void {
|
||||
g.write('((u8*)')
|
||||
g.gen_expr(base_id)
|
||||
g.write(')[')
|
||||
g.gen_expr(g.a.child(&lhs, 1))
|
||||
g.write('] = ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
g.writeln(';')
|
||||
return
|
||||
}
|
||||
}
|
||||
mut arr_type := types.Array{}
|
||||
mut is_array_base := false
|
||||
if base_type is types.Array {
|
||||
arr_type = base_type
|
||||
is_array_base = true
|
||||
} else if base_type is types.Pointer {
|
||||
ptr_type := base_type
|
||||
ptr_base := ptr_type.base_type
|
||||
if ptr_base is types.Array {
|
||||
arr_type = ptr_base
|
||||
is_array_base = true
|
||||
}
|
||||
}
|
||||
if is_array_base {
|
||||
c_elem := g.tc.c_type(arr_type.elem_type)
|
||||
g.write('array_set(')
|
||||
if base_type is types.Pointer {
|
||||
g.write('*')
|
||||
}
|
||||
g.gen_expr(base_id)
|
||||
g.write(', ')
|
||||
g.gen_expr(g.a.child(&lhs, 1))
|
||||
g.write(', &(${c_elem}[]){')
|
||||
g.gen_expr_with_expected_type(g.a.child(&node, 1), arr_type.elem_type)
|
||||
g.writeln('});')
|
||||
return
|
||||
}
|
||||
}
|
||||
g.gen_assign(node)
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,405 @@
|
||||
module c
|
||||
|
||||
import runtime
|
||||
import strings
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
const max_flat_cgen_jobs = 3
|
||||
const min_flat_cgen_parallel_items = 1024
|
||||
|
||||
struct FlatFnGenItem {
|
||||
node_id flat.NodeId
|
||||
module string
|
||||
cost int
|
||||
}
|
||||
|
||||
$if !windows {
|
||||
struct FlatCgenChunkArgs {
|
||||
worker voidptr
|
||||
work_items_ptr voidptr
|
||||
}
|
||||
|
||||
@[typedef]
|
||||
struct C.pthread_t {}
|
||||
|
||||
fn C.pthread_create(thread &C.pthread_t, attr voidptr, start_routine fn (voidptr) voidptr, arg voidptr) int
|
||||
fn C.pthread_join(thread C.pthread_t, retval voidptr) int
|
||||
fn C.pthread_attr_init(attr voidptr) int
|
||||
fn C.pthread_attr_setstacksize(attr voidptr, stacksize usize) int
|
||||
fn C.pthread_attr_destroy(attr voidptr) int
|
||||
|
||||
fn flat_cgen_chunk_thread(arg voidptr) voidptr {
|
||||
a := unsafe { &FlatCgenChunkArgs(arg) }
|
||||
mut w := unsafe { &FlatGen(a.worker) }
|
||||
items := unsafe { &[]FlatFnGenItem(a.work_items_ptr) }
|
||||
w.gen_fn_items(*items)
|
||||
return unsafe { nil }
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_fns_dispatch(no_parallel bool) {
|
||||
if no_parallel {
|
||||
g.gen_fns()
|
||||
return
|
||||
}
|
||||
items := g.collect_fn_gen_items()
|
||||
n_items := items.len
|
||||
n_jobs := flat_cgen_job_count(runtime.nr_jobs(), n_items)
|
||||
$if windows {
|
||||
g.gen_fn_items(items)
|
||||
return
|
||||
} $else {
|
||||
if n_items < min_flat_cgen_parallel_items || n_jobs <= 1 {
|
||||
g.gen_fn_items(items)
|
||||
return
|
||||
}
|
||||
g.parallel_used = true
|
||||
g.prepare_parallel_items(items)
|
||||
mut chunk_items := split_flat_cgen_items(items, n_jobs)
|
||||
chunk_count := chunk_items.len
|
||||
mut thread_ids := []C.pthread_t{len: chunk_count}
|
||||
mut args := []FlatCgenChunkArgs{cap: chunk_count}
|
||||
mut workers := []voidptr{cap: chunk_count}
|
||||
|
||||
for ci := 0; ci < chunk_count; ci++ {
|
||||
w := g.new_parallel_worker(ci)
|
||||
workers << voidptr(w)
|
||||
}
|
||||
for ci := 0; ci < chunk_count; ci++ {
|
||||
args << FlatCgenChunkArgs{
|
||||
worker: workers[ci]
|
||||
work_items_ptr: unsafe { voidptr(&chunk_items[ci]) }
|
||||
}
|
||||
}
|
||||
|
||||
attr_buf := [64]u8{}
|
||||
attr := unsafe { voidptr(&attr_buf[0]) }
|
||||
C.pthread_attr_init(attr)
|
||||
C.pthread_attr_setstacksize(attr, 64 * 1024 * 1024)
|
||||
for ci := 0; ci < chunk_count; ci++ {
|
||||
C.pthread_create(unsafe { &thread_ids[ci] }, attr, flat_cgen_chunk_thread,
|
||||
unsafe { voidptr(&args[ci]) })
|
||||
}
|
||||
C.pthread_attr_destroy(attr)
|
||||
for ci := 0; ci < chunk_count; ci++ {
|
||||
C.pthread_join(thread_ids[ci], unsafe { nil })
|
||||
}
|
||||
for ci := 0; ci < chunk_count; ci++ {
|
||||
w := unsafe { &FlatGen(workers[ci]) }
|
||||
g.merge_parallel_worker(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flat_cgen_job_count(n_runtime_jobs int, n_items int) int {
|
||||
if n_runtime_jobs <= 0 || n_items <= 0 {
|
||||
return 0
|
||||
}
|
||||
mut n_jobs := n_runtime_jobs
|
||||
if n_jobs > max_flat_cgen_jobs {
|
||||
n_jobs = max_flat_cgen_jobs
|
||||
}
|
||||
if n_jobs > n_items {
|
||||
n_jobs = n_items
|
||||
}
|
||||
return n_jobs
|
||||
}
|
||||
|
||||
fn split_flat_cgen_items(items []FlatFnGenItem, n_jobs int) [][]FlatFnGenItem {
|
||||
mut chunks := [][]FlatFnGenItem{}
|
||||
if n_jobs <= 0 || items.len == 0 {
|
||||
return chunks
|
||||
}
|
||||
mut total_cost := 0
|
||||
for item in items {
|
||||
total_cost += item.cost
|
||||
}
|
||||
mut target_cost := total_cost / n_jobs
|
||||
if total_cost % n_jobs != 0 {
|
||||
target_cost++
|
||||
}
|
||||
if target_cost < 1 {
|
||||
target_cost = 1
|
||||
}
|
||||
mut current := []FlatFnGenItem{}
|
||||
mut current_cost := 0
|
||||
mut chunks_left := n_jobs
|
||||
for idx, item in items {
|
||||
remaining_items := items.len - idx
|
||||
if current.len > 0 && current_cost >= target_cost && chunks_left > 1
|
||||
&& remaining_items >= chunks_left {
|
||||
chunks << current
|
||||
current = []FlatFnGenItem{}
|
||||
current_cost = 0
|
||||
chunks_left--
|
||||
}
|
||||
current << item
|
||||
current_cost += item.cost
|
||||
}
|
||||
if current.len > 0 {
|
||||
chunks << current
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) collect_fn_gen_items() []FlatFnGenItem {
|
||||
mut items := []FlatFnGenItem{}
|
||||
mut cur_module := ''
|
||||
for i in 0 .. g.a.nodes.len {
|
||||
node := g.a.nodes[i]
|
||||
kind_id := node_kind_id(node)
|
||||
if kind_id == 77 {
|
||||
cur_module = ''
|
||||
g.tc.cur_module = cur_module
|
||||
continue
|
||||
}
|
||||
if kind_id == 73 {
|
||||
cur_module = node.value
|
||||
g.tc.cur_module = cur_module
|
||||
continue
|
||||
}
|
||||
|
||||
if kind_id != 61 {
|
||||
continue
|
||||
}
|
||||
if !g.should_emit_fn_node_in_module(node, i, cur_module) {
|
||||
continue
|
||||
}
|
||||
qfn := qualified_fn_name_in_module(cur_module, node.value)
|
||||
if g.emitted_fn_contains(qfn) {
|
||||
continue
|
||||
}
|
||||
g.emitted_fns[qfn] = true
|
||||
items << FlatFnGenItem{
|
||||
node_id: flat.NodeId(i)
|
||||
module: cur_module
|
||||
cost: node.children_count + 1
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_fn_items(items []FlatFnGenItem) {
|
||||
for item in items {
|
||||
if int(item.node_id) < 0 || int(item.node_id) >= g.a.nodes.len {
|
||||
continue
|
||||
}
|
||||
g.tc.cur_module = item.module
|
||||
node := g.a.nodes[int(item.node_id)]
|
||||
g.gen_fn_in_module(node, item.module)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) prepare_parallel_items(items []FlatFnGenItem) {
|
||||
for item in items {
|
||||
g.tc.cur_module = item.module
|
||||
g.prepare_parallel_node(item.node_id)
|
||||
}
|
||||
g.register_interface_strings()
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) prepare_parallel_node(id flat.NodeId) {
|
||||
if int(id) < 0 || int(id) >= g.a.nodes.len {
|
||||
return
|
||||
}
|
||||
node := g.a.nodes[int(id)]
|
||||
if node.kind == .string_literal {
|
||||
g.intern_string(node.value)
|
||||
}
|
||||
if g.should_preseed_parallel_type_text(node.typ) {
|
||||
g.preseed_parallel_fn_ptr_type(g.tc.parse_type(node.typ))
|
||||
}
|
||||
if expr_type := g.tc.expr_type(id) {
|
||||
g.preseed_parallel_fn_ptr_type(expr_type)
|
||||
}
|
||||
for i in 0 .. node.children_count {
|
||||
g.prepare_parallel_node(g.a.child(&node, i))
|
||||
}
|
||||
}
|
||||
|
||||
fn (g &FlatGen) should_preseed_parallel_type_text(typ string) bool {
|
||||
if typ.len == 0 {
|
||||
return false
|
||||
}
|
||||
clean := g.parallel_base_type_text(typ)
|
||||
if clean.starts_with('fn(') || clean.starts_with('fn (') {
|
||||
return true
|
||||
}
|
||||
if clean in g.tc.type_aliases {
|
||||
return true
|
||||
}
|
||||
qtyp := g.tc.qualify_name(clean)
|
||||
return qtyp in g.tc.type_aliases
|
||||
}
|
||||
|
||||
fn (g &FlatGen) parallel_base_type_text(typ string) string {
|
||||
mut clean := typ.trim_space()
|
||||
for clean.len > 0 {
|
||||
if clean.starts_with('shared ') {
|
||||
clean = clean[7..].trim_space()
|
||||
} else if clean[0] == `&` || clean[0] == `?` || clean[0] == `!` {
|
||||
clean = clean[1..].trim_space()
|
||||
} else if clean.starts_with('...') {
|
||||
clean = clean[3..].trim_space()
|
||||
} else if clean.starts_with('[]') {
|
||||
clean = clean[2..].trim_space()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return clean
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) preseed_parallel_fn_ptr_type(typ types.Type) {
|
||||
if typ is types.FnType {
|
||||
g.resolve_fn_ptr_type(g.fn_ptr_type_key(typ))
|
||||
for param in typ.params {
|
||||
g.preseed_parallel_fn_ptr_type(param)
|
||||
}
|
||||
g.preseed_parallel_fn_ptr_type(typ.return_type)
|
||||
} else if typ is types.Pointer {
|
||||
g.preseed_parallel_fn_ptr_type(typ.base_type)
|
||||
} else if typ is types.Array {
|
||||
g.preseed_parallel_fn_ptr_type(typ.elem_type)
|
||||
} else if typ is types.ArrayFixed {
|
||||
g.preseed_parallel_fn_ptr_type(typ.elem_type)
|
||||
} else if typ is types.Map {
|
||||
g.preseed_parallel_fn_ptr_type(typ.key_type)
|
||||
g.preseed_parallel_fn_ptr_type(typ.value_type)
|
||||
} else if typ is types.OptionType {
|
||||
g.preseed_parallel_fn_ptr_type(typ.base_type)
|
||||
} else if typ is types.ResultType {
|
||||
g.preseed_parallel_fn_ptr_type(typ.base_type)
|
||||
} else if typ is types.Alias {
|
||||
g.preseed_parallel_fn_ptr_type(typ.base_type)
|
||||
} else if typ is types.MultiReturn {
|
||||
for item in typ.types {
|
||||
g.preseed_parallel_fn_ptr_type(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) fn_ptr_type_key(typ types.FnType) string {
|
||||
ret := if typ.return_type is types.Void { 'void' } else { g.tc.c_type(typ.return_type) }
|
||||
if typ.params.len == 0 {
|
||||
return 'fn_ptr:${ret}|void'
|
||||
}
|
||||
mut params := []string{}
|
||||
for param in typ.params {
|
||||
params << g.tc.c_type(param)
|
||||
}
|
||||
return 'fn_ptr:${ret}|${params.join(', ')}'
|
||||
}
|
||||
|
||||
fn (g &FlatGen) new_parallel_worker(worker_id int) &FlatGen {
|
||||
return &FlatGen{
|
||||
sb: strings.new_builder(64_000)
|
||||
a: unsafe { g.a }
|
||||
used_fns: g.used_fns.clone()
|
||||
used_fn_names: g.used_fn_names.clone()
|
||||
str_lits: g.str_lits.clone()
|
||||
str_lit_ids: g.str_lit_ids.clone()
|
||||
global_types: g.global_types.clone()
|
||||
enum_vals: g.enum_vals.clone()
|
||||
interfaces: g.interfaces.clone()
|
||||
const_vals: g.const_vals.clone()
|
||||
const_modules: g.const_modules.clone()
|
||||
const_init_order: g.const_init_order.clone()
|
||||
global_modules: g.global_modules.clone()
|
||||
global_inits: g.global_inits.clone()
|
||||
global_init_order: g.global_init_order.clone()
|
||||
iface_impls: g.iface_impls.clone()
|
||||
iface_type_ids: g.iface_type_ids.clone()
|
||||
module_init_fns: g.module_init_fns.clone()
|
||||
module_init_fn_modules: g.module_init_fn_modules.clone()
|
||||
module_imports: g.module_imports.clone()
|
||||
tc: g.clone_parallel_type_checker()
|
||||
has_builtins: g.has_builtins
|
||||
tmp_count: (worker_id + 1) * 100_000
|
||||
line_start: true
|
||||
modules: g.modules.clone()
|
||||
fn_ptr_types: g.fn_ptr_types.clone()
|
||||
fn_decl_param_types: g.fn_decl_param_types.clone()
|
||||
struct_decl_infos: g.struct_decl_infos.clone()
|
||||
struct_decl_short_infos: g.struct_decl_short_infos.clone()
|
||||
runtime_inits: g.runtime_inits.clone()
|
||||
compiler_vroot: g.compiler_vroot
|
||||
cur_param_names: g.cur_param_names.clone()
|
||||
cur_param_type_values: g.cur_param_type_values.clone()
|
||||
cur_param_types: g.cur_param_types.clone()
|
||||
cur_fn_ret: g.cur_fn_ret
|
||||
cur_fn_ret_is_optional: g.cur_fn_ret_is_optional
|
||||
cur_fn_ret_base: g.cur_fn_ret_base
|
||||
expected_expr_type: g.expected_expr_type
|
||||
expected_enum: g.expected_enum
|
||||
needed_optional_types: g.needed_optional_types.clone()
|
||||
emitted_optional_types: g.emitted_optional_types.clone()
|
||||
emitted_fns: g.emitted_fns.clone()
|
||||
array_method_cache: g.array_method_cache.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn (g &FlatGen) clone_parallel_type_checker() &types.TypeChecker {
|
||||
mut fs := types.new_scope(unsafe { nil })
|
||||
fs.names = g.tc.file_scope.names.clone()
|
||||
fs.types = g.tc.file_scope.types.clone()
|
||||
return &types.TypeChecker{
|
||||
a: unsafe { g.tc.a }
|
||||
fn_ret_types: g.tc.fn_ret_types.clone()
|
||||
fn_param_types: g.tc.fn_param_types.clone()
|
||||
fn_variadic: g.tc.fn_variadic.clone()
|
||||
structs: g.tc.structs.clone()
|
||||
unions: g.tc.unions.clone()
|
||||
type_aliases: g.tc.type_aliases.clone()
|
||||
sum_types: g.tc.sum_types.clone()
|
||||
enum_names: g.tc.enum_names.clone()
|
||||
enum_fields: g.tc.enum_fields.clone()
|
||||
flag_enums: g.tc.flag_enums.clone()
|
||||
interface_names: g.tc.interface_names.clone()
|
||||
interface_fields: g.tc.interface_fields.clone()
|
||||
interface_embeds: g.tc.interface_embeds.clone()
|
||||
interface_abstract_methods: g.tc.interface_abstract_methods.clone()
|
||||
c_globals: g.tc.c_globals.clone()
|
||||
const_types: g.tc.const_types.clone()
|
||||
const_exprs: g.tc.const_exprs.clone()
|
||||
const_modules: g.tc.const_modules.clone()
|
||||
const_suffixes: g.tc.const_suffixes.clone()
|
||||
imports: g.tc.imports.clone()
|
||||
file_imports: g.tc.file_imports.clone()
|
||||
file_modules: g.tc.file_modules.clone()
|
||||
file_scope: fs
|
||||
cur_scope: fs
|
||||
scope_pool: []&types.Scope{}
|
||||
has_builtins: g.tc.has_builtins
|
||||
cur_module: g.tc.cur_module
|
||||
cur_file: g.tc.cur_file
|
||||
errors: g.tc.errors.clone()
|
||||
resolved_call_names: g.tc.resolved_call_names.clone()
|
||||
resolved_call_set: g.tc.resolved_call_set.clone()
|
||||
expr_type_values: g.tc.expr_type_values.clone()
|
||||
expr_type_set: g.tc.expr_type_set.clone()
|
||||
checking_nodes: g.tc.checking_nodes.clone()
|
||||
diagnose_unknown_calls: g.tc.diagnose_unknown_calls
|
||||
reject_unlowered_map_mutation: g.tc.reject_unlowered_map_mutation
|
||||
diagnostic_files: g.tc.diagnostic_files.clone()
|
||||
cur_fn_ret_type: g.tc.cur_fn_ret_type
|
||||
smartcasts: g.tc.smartcasts.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) merge_parallel_worker(w &FlatGen) {
|
||||
mut ww := unsafe { w }
|
||||
worker_output := ww.sb.str()
|
||||
if worker_output.len > 0 {
|
||||
g.sb.write_string(worker_output)
|
||||
}
|
||||
for opt_name, val_type in w.needed_optional_types {
|
||||
g.needed_optional_types[opt_name] = val_type
|
||||
}
|
||||
for encoded, name in w.fn_ptr_types {
|
||||
if encoded !in g.fn_ptr_types {
|
||||
g.fn_ptr_types[encoded] = name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module c
|
||||
|
||||
fn (mut g FlatGen) gen_fns_dispatch(_no_parallel bool) {
|
||||
g.gen_fns()
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
module c
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
fn (mut g FlatGen) gen_for(node flat.Node) {
|
||||
g.tc.push_scope()
|
||||
defer_start := g.defers.len
|
||||
init_node := g.a.child_node(&node, 0)
|
||||
cond_id := g.a.child(&node, 1)
|
||||
cond_node := g.a.nodes[int(cond_id)]
|
||||
post_node := g.a.child_node(&node, 2)
|
||||
|
||||
if init_node.kind == .empty && cond_node.kind == .empty && post_node.kind == .empty {
|
||||
g.writeln('for (;;) {')
|
||||
} else if init_node.kind == .empty && post_node.kind == .empty {
|
||||
g.write('while (')
|
||||
g.gen_expr(cond_id)
|
||||
g.writeln(') {')
|
||||
} else {
|
||||
g.write('for (')
|
||||
if init_node.kind != .empty {
|
||||
g.gen_node_inline(g.a.child(&node, 0))
|
||||
}
|
||||
g.write('; ')
|
||||
if cond_node.kind != .empty {
|
||||
g.gen_expr(cond_id)
|
||||
}
|
||||
g.write('; ')
|
||||
if post_node.kind != .empty {
|
||||
g.gen_node_inline(g.a.child(&node, 2))
|
||||
}
|
||||
g.writeln(') {')
|
||||
}
|
||||
g.indent++
|
||||
for i in 3 .. 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.writeln('}')
|
||||
g.tc.pop_scope()
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_for_in(node flat.Node) {
|
||||
g.tc.push_scope()
|
||||
header_count := node.value.int()
|
||||
val_id := g.a.child(&node, 1)
|
||||
var_node := if int(val_id) >= 0 {
|
||||
g.a.child_node(&node, 1)
|
||||
} else {
|
||||
g.a.child_node(&node, 0)
|
||||
}
|
||||
var_name := c_name(var_node.value)
|
||||
g.tc.cur_scope.insert(var_node.value, types.Type(types.int_))
|
||||
body_start := header_count
|
||||
|
||||
if header_count == 4 {
|
||||
panic('internal error: range for-in reached C backend after transform')
|
||||
} else if header_count == 3 {
|
||||
container := g.a.child_node(&node, 2)
|
||||
if container.kind == .range {
|
||||
panic('internal error: range for-in reached C backend after transform')
|
||||
} else {
|
||||
container_type := g.tc.resolve_type(g.a.child(&node, 2))
|
||||
has_index := int(val_id) >= 0
|
||||
idx_var := if has_index {
|
||||
c_name(g.a.child_node(&node, 0).value)
|
||||
} else {
|
||||
'__iter_${var_name}'
|
||||
}
|
||||
elem_var := if has_index {
|
||||
c_name(g.a.child_node(&node, 1).value)
|
||||
} else {
|
||||
var_name
|
||||
}
|
||||
clean_container_type := types.unwrap_pointer(container_type)
|
||||
if clean_container_type is types.Map {
|
||||
c_key := g.tc.c_type(clean_container_type.key_type)
|
||||
c_val := g.tc.c_type(clean_container_type.value_type)
|
||||
container_str := g.expr_to_string(g.a.child(&node, 2))
|
||||
iter_var := '__mi_${g.tmp_count}'
|
||||
g.tmp_count++
|
||||
key_var := if has_index { idx_var } else { '__mk_${g.tmp_count}' }
|
||||
val_var_ := if has_index { elem_var } else { var_name }
|
||||
access := if container_type is types.Pointer { '->' } else { '.' }
|
||||
key_values := '${container_str}${access}key_values'
|
||||
g.writeln('for (int ${iter_var} = 0; ${iter_var} < ${key_values}.len; ${iter_var}++) {')
|
||||
g.indent++
|
||||
g.writeln('if (${key_values}.all_deleted && ${key_values}.all_deleted[${iter_var}]) continue;')
|
||||
g.writeln('${c_key} ${key_var} = *(${c_key}*)(${key_values}.keys + ${iter_var} * ${key_values}.key_bytes);')
|
||||
g.writeln('${c_val} ${val_var_} = *(${c_val}*)(${key_values}.values + ${iter_var} * ${key_values}.value_bytes);')
|
||||
if has_index {
|
||||
g.tc.cur_scope.insert(key_var, clean_container_type.key_type)
|
||||
}
|
||||
g.tc.cur_scope.insert(val_var_, clean_container_type.value_type)
|
||||
} else if container_type is types.Array {
|
||||
c_elem := g.tc.c_type(container_type.elem_type)
|
||||
container_str := g.expr_to_string(g.a.child(&node, 2))
|
||||
g.writeln('for (int ${idx_var} = 0; ${idx_var} < ${container_str}.len; ${idx_var}++) {')
|
||||
g.indent++
|
||||
g.writeln('${c_elem} ${elem_var} = *(${c_elem}*)array_get(${container_str}, ${idx_var});')
|
||||
g.tc.cur_scope.insert(elem_var, container_type.elem_type)
|
||||
} else if container_type is types.String {
|
||||
container_str := g.expr_to_string(g.a.child(&node, 2))
|
||||
g.writeln('for (int ${idx_var} = 0; ${idx_var} < ${container_str}.len; ${idx_var}++) {')
|
||||
g.indent++
|
||||
g.writeln('u8 ${elem_var} = ((u8*)${container_str}.str)[${idx_var}];')
|
||||
g.tc.cur_scope.insert(elem_var, types.Type(types.u8_))
|
||||
} else if container_type is types.ArrayFixed {
|
||||
af := container_type
|
||||
c_elem := g.tc.c_type(af.elem_type)
|
||||
arr_len := g.fixed_array_len_value(af)
|
||||
g.writeln('for (int ${idx_var} = 0; ${idx_var} < ${arr_len}; ${idx_var}++) {')
|
||||
g.indent++
|
||||
g.write('${c_elem} ${elem_var} = ')
|
||||
g.gen_expr(g.a.child(&node, 2))
|
||||
g.writeln('[${idx_var}];')
|
||||
g.tc.cur_scope.insert(elem_var, af.elem_type)
|
||||
} else {
|
||||
g.writeln('for (int ${idx_var} = 0; ${idx_var} < 0; ${idx_var}++) {')
|
||||
g.indent++
|
||||
g.writeln('int ${elem_var} = 0;')
|
||||
g.tc.cur_scope.insert(elem_var, types.Type(types.int_))
|
||||
}
|
||||
if has_index && container_type !is types.Map {
|
||||
g.tc.cur_scope.insert(idx_var, types.Type(types.int_))
|
||||
}
|
||||
for i in body_start .. node.children_count {
|
||||
g.gen_node(g.a.child(&node, i))
|
||||
}
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
g.tc.pop_scope()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
g.tc.pop_scope()
|
||||
return
|
||||
}
|
||||
g.indent++
|
||||
for i in body_start .. node.children_count {
|
||||
g.gen_node(g.a.child(&node, i))
|
||||
}
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
g.tc.pop_scope()
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_node_inline(id flat.NodeId) {
|
||||
node := g.a.nodes[int(id)]
|
||||
match node.kind {
|
||||
.expr_stmt {
|
||||
g.gen_expr(g.a.child(&node, 0))
|
||||
}
|
||||
.decl_assign {
|
||||
lhs_id := g.a.child(&node, 0)
|
||||
rhs_id := g.a.child(&node, 1)
|
||||
lhs := g.a.nodes[int(lhs_id)]
|
||||
v_type := g.tc.resolve_type(rhs_id)
|
||||
typ := g.tc.c_type(v_type)
|
||||
g.write('${typ} ')
|
||||
g.gen_expr(lhs_id)
|
||||
g.write(' = ')
|
||||
g.gen_expr(rhs_id)
|
||||
if lhs.kind == .ident {
|
||||
g.tc.cur_scope.insert(lhs.value, v_type)
|
||||
}
|
||||
}
|
||||
.assign {
|
||||
g.gen_expr(g.a.child(&node, 0))
|
||||
g.write(' ${g.op_str(node.op)} ')
|
||||
g.gen_expr(g.a.child(&node, 1))
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
module c
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
fn (mut g FlatGen) gen_if(node flat.Node) {
|
||||
if node.children_count < 2 {
|
||||
return
|
||||
}
|
||||
cond_id := g.a.child(&node, 0)
|
||||
if !g.valid_node_id(cond_id) {
|
||||
return
|
||||
}
|
||||
cond := g.a.nodes[int(cond_id)]
|
||||
if cond.kind == .decl_assign {
|
||||
g.gen_if_guard(node, cond)
|
||||
return
|
||||
}
|
||||
if cond.kind != .empty {
|
||||
g.write('if (')
|
||||
g.gen_expr(cond_id)
|
||||
g.writeln(') {')
|
||||
} else {
|
||||
g.writeln('{')
|
||||
}
|
||||
g.tc.push_scope()
|
||||
defer_start := g.defers.len
|
||||
g.indent++
|
||||
if cond.kind == .is_expr {
|
||||
g.smartcast_is_expr(&cond)
|
||||
}
|
||||
then_id := g.a.child(&node, 1)
|
||||
if g.valid_node_id(then_id) {
|
||||
then_block := g.a.nodes[int(then_id)]
|
||||
for i in 0 .. then_block.children_count {
|
||||
child_id := g.a.child(&then_block, i)
|
||||
if g.valid_node_id(child_id) {
|
||||
g.gen_node(child_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
g.gen_defers_from(defer_start)
|
||||
g.trim_defers(defer_start)
|
||||
g.indent--
|
||||
g.tc.pop_scope()
|
||||
g.gen_if_else(node)
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) smartcast_is_expr(cond &flat.Node) {
|
||||
expr_id := g.a.child(cond, 0)
|
||||
expr_node := g.a.nodes[int(expr_id)]
|
||||
if expr_node.kind == .ident {
|
||||
sum_type := g.tc.resolve_type(expr_id)
|
||||
clean_sum0 := types.unwrap_pointer(sum_type)
|
||||
mut clean_sum := clean_sum0
|
||||
if clean_sum0 is types.Alias {
|
||||
clean_sum = clean_sum0.base_type
|
||||
}
|
||||
if clean_sum is types.SumType {
|
||||
variant_name := g.resolve_variant(clean_sum.name, cond.value)
|
||||
variant_type := g.tc.parse_type(variant_name)
|
||||
if variant_type is types.Void {
|
||||
return
|
||||
}
|
||||
variant_ct := g.tc.c_type(variant_type)
|
||||
field_name := g.sum_field_name(variant_name)
|
||||
is_ptr_variant := g.variant_references_sum(variant_name, clean_sum.name)
|
||||
var_name := c_name(expr_node.value)
|
||||
tmp := g.tmp_name()
|
||||
if is_ptr_variant {
|
||||
g.writeln('${variant_ct} ${tmp} = *${var_name}.${field_name};')
|
||||
} else {
|
||||
g.writeln('${variant_ct} ${tmp} = ${var_name}.${field_name};')
|
||||
}
|
||||
g.writeln('${variant_ct} ${var_name} = ${tmp};')
|
||||
g.tc.cur_scope.insert(expr_node.value, variant_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (g &FlatGen) expr_key(id flat.NodeId) string {
|
||||
node := g.a.nodes[int(id)]
|
||||
if node.kind == .ident {
|
||||
return node.value
|
||||
}
|
||||
if node.kind == .selector && node.children_count > 0 {
|
||||
base_key := g.expr_key(g.a.child(&node, 0))
|
||||
if base_key.len > 0 {
|
||||
return '${base_key}.${node.value}'
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_if_guard(node flat.Node, cond flat.Node) {
|
||||
if cond.children_count < 2 {
|
||||
return
|
||||
}
|
||||
lhs_id := g.a.child(&cond, 0)
|
||||
rhs_id := g.a.child(&cond, 1)
|
||||
if !g.valid_node_id(lhs_id) || !g.valid_node_id(rhs_id) {
|
||||
return
|
||||
}
|
||||
lhs := g.a.nodes[int(lhs_id)]
|
||||
rhs := g.a.nodes[int(rhs_id)]
|
||||
var_name := c_name(lhs.value)
|
||||
tmp := g.tmp_name()
|
||||
defer_start := g.defers.len
|
||||
if rhs.kind == .index {
|
||||
base_id := g.a.child(rhs, 0)
|
||||
base_type := g.tc.resolve_type(base_id)
|
||||
if base_type is types.Map {
|
||||
c_val_type := g.tc.c_type(base_type.value_type)
|
||||
c_key_type := g.tc.c_type(base_type.key_type)
|
||||
g.write('void* ${tmp} = map__get_check(&')
|
||||
g.gen_expr(base_id)
|
||||
g.write(', &(${c_key_type}[]){')
|
||||
g.gen_expr(g.a.child(rhs, 1))
|
||||
g.writeln('});')
|
||||
g.writeln('if (${tmp} != NULL) {')
|
||||
g.tc.push_scope()
|
||||
g.indent++
|
||||
g.writeln('${c_val_type} ${var_name} = *(${c_val_type}*)${tmp};')
|
||||
g.tc.cur_scope.insert(lhs.value, base_type.value_type)
|
||||
} else {
|
||||
rhs_type := g.tc.resolve_type(rhs_id)
|
||||
opt_ct := g.optional_type_name(rhs_type)
|
||||
val_ct, val_type := g.optional_value_ct(rhs_type)
|
||||
g.write('${opt_ct} ${tmp} = ')
|
||||
g.gen_expr(rhs_id)
|
||||
g.writeln(';')
|
||||
g.writeln('if (${tmp}.ok) {')
|
||||
g.tc.push_scope()
|
||||
g.indent++
|
||||
g.writeln('${val_ct} ${var_name} = ${tmp}.value;')
|
||||
g.tc.cur_scope.insert(lhs.value, val_type)
|
||||
}
|
||||
} else {
|
||||
rhs_type := g.tc.resolve_type(rhs_id)
|
||||
opt_ct := g.optional_type_name(rhs_type)
|
||||
val_ct, val_type := g.optional_value_ct(rhs_type)
|
||||
g.write('${opt_ct} ${tmp} = ')
|
||||
g.gen_expr(rhs_id)
|
||||
g.writeln(';')
|
||||
g.writeln('if (${tmp}.ok) {')
|
||||
g.tc.push_scope()
|
||||
g.indent++
|
||||
g.writeln('${val_ct} ${var_name} = ${tmp}.value;')
|
||||
g.tc.cur_scope.insert(lhs.value, val_type)
|
||||
}
|
||||
then_id := g.a.child(&node, 1)
|
||||
if g.valid_node_id(then_id) {
|
||||
then_block := g.a.nodes[int(then_id)]
|
||||
for i in 0 .. then_block.children_count {
|
||||
child_id := g.a.child(&then_block, i)
|
||||
if g.valid_node_id(child_id) {
|
||||
g.gen_node(child_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
g.gen_defers_from(defer_start)
|
||||
g.trim_defers(defer_start)
|
||||
g.indent--
|
||||
g.tc.pop_scope()
|
||||
g.gen_if_else(node)
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_if_else(node flat.Node) {
|
||||
if node.children_count > 2 {
|
||||
else_id := g.a.child(&node, 2)
|
||||
if !g.valid_node_id(else_id) {
|
||||
g.writeln('}')
|
||||
return
|
||||
}
|
||||
else_node := g.a.nodes[int(else_id)]
|
||||
if else_node.kind == .if_expr {
|
||||
else_cond_id := g.a.child(&else_node, 0)
|
||||
else_cond_is_guard := if g.valid_node_id(else_cond_id) {
|
||||
g.a.nodes[int(else_cond_id)].kind == .decl_assign
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if else_cond_is_guard {
|
||||
g.writeln('} else {')
|
||||
g.tc.push_scope()
|
||||
g.indent++
|
||||
g.gen_if(else_node)
|
||||
g.indent--
|
||||
g.tc.pop_scope()
|
||||
g.writeln('}')
|
||||
} else {
|
||||
g.write('} else ')
|
||||
g.gen_if(else_node)
|
||||
}
|
||||
} else if else_node.kind == .block {
|
||||
g.writeln('} else {')
|
||||
g.tc.push_scope()
|
||||
defer_start := g.defers.len
|
||||
g.indent++
|
||||
for i in 0 .. else_node.children_count {
|
||||
child_id := g.a.child(&else_node, i)
|
||||
if g.valid_node_id(child_id) {
|
||||
g.gen_node(child_id)
|
||||
}
|
||||
}
|
||||
g.gen_defers_from(defer_start)
|
||||
g.trim_defers(defer_start)
|
||||
g.indent--
|
||||
g.tc.pop_scope()
|
||||
g.writeln('}')
|
||||
} else {
|
||||
g.writeln('}')
|
||||
}
|
||||
} else {
|
||||
g.writeln('}')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_if_expr(node flat.Node) {
|
||||
then_block := g.a.child_node(&node, 1)
|
||||
mut needs_stmt_expr := then_block.children_count > 1
|
||||
if !needs_stmt_expr && node.children_count > 2 {
|
||||
else_node := g.a.child_node(&node, 2)
|
||||
if else_node.kind == .block && else_node.children_count > 1 {
|
||||
needs_stmt_expr = true
|
||||
} else if else_node.kind == .if_expr {
|
||||
needs_stmt_expr = true
|
||||
}
|
||||
}
|
||||
if needs_stmt_expr {
|
||||
g.gen_if_expr_stmt(node)
|
||||
return
|
||||
}
|
||||
g.write('(')
|
||||
g.gen_expr(g.a.child(&node, 0))
|
||||
g.write(' ? ')
|
||||
if then_block.children_count > 0 {
|
||||
last := g.a.child_node(then_block, then_block.children_count - 1)
|
||||
if last.kind == .expr_stmt {
|
||||
g.gen_expr(g.a.child(last, 0))
|
||||
} else {
|
||||
g.gen_expr(g.a.child(then_block, then_block.children_count - 1))
|
||||
}
|
||||
}
|
||||
g.write(' : ')
|
||||
if node.children_count > 2 {
|
||||
else_node := g.a.child_node(&node, 2)
|
||||
if else_node.kind == .if_expr {
|
||||
g.gen_if_expr(*else_node)
|
||||
} else if else_node.kind == .block {
|
||||
if else_node.children_count > 0 {
|
||||
last := g.a.child_node(else_node, else_node.children_count - 1)
|
||||
if last.kind == .expr_stmt {
|
||||
g.gen_expr(g.a.child(last, 0))
|
||||
} else {
|
||||
g.gen_expr(g.a.child(else_node, else_node.children_count - 1))
|
||||
}
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
g.write(')')
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_if_expr_block(block &flat.Node) {
|
||||
for i in 0 .. block.children_count {
|
||||
child_id := g.a.child(block, i)
|
||||
child := g.a.nodes[int(child_id)]
|
||||
if i == block.children_count - 1 {
|
||||
if child.kind == .expr_stmt {
|
||||
g.write('_ifexpr = ')
|
||||
g.gen_expr(g.a.child(child, 0))
|
||||
g.writeln(';')
|
||||
} else if child.kind == .if_expr {
|
||||
g.write('_ifexpr = ')
|
||||
g.gen_if_expr(child)
|
||||
g.writeln(';')
|
||||
} else if g.is_expr_kind(child.kind) {
|
||||
g.write('_ifexpr = ')
|
||||
g.gen_expr(child_id)
|
||||
g.writeln(';')
|
||||
} else {
|
||||
g.gen_node(child_id)
|
||||
}
|
||||
} else {
|
||||
g.gen_node(child_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (g &FlatGen) is_expr_kind(kind flat.NodeKind) bool {
|
||||
return match kind {
|
||||
.int_literal, .float_literal, .bool_literal, .char_literal, .string_literal,
|
||||
.string_interp, .ident, .infix, .prefix, .postfix, .paren, .call, .selector, .index,
|
||||
.if_expr, .struct_init, .field_init, .array_literal, .array_init, .map_init, .fn_literal,
|
||||
.or_expr, .cast_expr, .as_expr, .enum_val, .assoc, .range, .nil_literal, .none_expr,
|
||||
.spawn_expr, .lock_expr, .lambda_expr, .sizeof_expr, .typeof_expr, .dump_expr,
|
||||
.offsetof_expr, .is_expr, .in_expr {
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) seed_scope_from_decl(node flat.Node) {
|
||||
if node.kind != .decl_assign || node.children_count < 2 {
|
||||
return
|
||||
}
|
||||
lhs := g.a.child_node(&node, 0)
|
||||
if lhs.kind != .ident || lhs.value.len == 0 {
|
||||
return
|
||||
}
|
||||
rhs_id := g.a.child(&node, 1)
|
||||
g.tc.cur_scope.insert(lhs.value, g.tc.resolve_type(rhs_id))
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) if_expr_block_tail_type(block &flat.Node) types.Type {
|
||||
if block.children_count == 0 {
|
||||
return types.Type(types.void_)
|
||||
}
|
||||
g.tc.push_scope()
|
||||
for i in 0 .. block.children_count - 1 {
|
||||
g.seed_scope_from_decl(*g.a.child_node(block, i))
|
||||
}
|
||||
last := g.a.child_node(block, block.children_count - 1)
|
||||
ret := if last.kind == .expr_stmt {
|
||||
g.tc.resolve_type(g.a.child(last, 0))
|
||||
} else {
|
||||
g.tc.resolve_type(g.a.child(block, block.children_count - 1))
|
||||
}
|
||||
g.tc.pop_scope()
|
||||
return ret
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) if_expr_type(node &flat.Node) types.Type {
|
||||
if node.children_count < 2 {
|
||||
return types.Type(types.void_)
|
||||
}
|
||||
then_block := g.a.child_node(node, 1)
|
||||
mut ret_type := g.if_expr_block_tail_type(then_block)
|
||||
if ret_type is types.Primitive && node.children_count > 2 {
|
||||
else_node := g.a.child_node(node, 2)
|
||||
if else_node.kind == .block && else_node.children_count > 0 {
|
||||
et := g.if_expr_block_tail_type(else_node)
|
||||
if et !is types.Primitive {
|
||||
ret_type = et
|
||||
}
|
||||
} else if else_node.kind == .if_expr && else_node.children_count > 2 {
|
||||
inner_else := g.a.child_node(else_node, 2)
|
||||
if inner_else.kind == .block && inner_else.children_count > 0 {
|
||||
et := g.if_expr_block_tail_type(inner_else)
|
||||
if et !is types.Primitive {
|
||||
ret_type = et
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret_type
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_if_expr_stmt(node flat.Node) {
|
||||
ret_type := g.if_expr_type(&node)
|
||||
then_block := g.a.child_node(&node, 1)
|
||||
ct := g.tc.c_type(ret_type)
|
||||
g.writeln('({${ct} _ifexpr;')
|
||||
g.write('if (')
|
||||
g.gen_expr(g.a.child(&node, 0))
|
||||
g.writeln(') {')
|
||||
g.gen_if_expr_block(then_block)
|
||||
g.write('} else ')
|
||||
if node.children_count > 2 {
|
||||
else_node := g.a.child_node(&node, 2)
|
||||
if else_node.kind == .if_expr {
|
||||
g.gen_if_expr_else_if(*else_node)
|
||||
} else if else_node.kind == .block {
|
||||
g.writeln('{')
|
||||
g.gen_if_expr_block(else_node)
|
||||
g.writeln('}')
|
||||
}
|
||||
} else {
|
||||
g.writeln('{ _ifexpr = (${ct}){0}; }')
|
||||
}
|
||||
g.write('_ifexpr;})')
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_if_expr_else_if(node flat.Node) {
|
||||
then_block := g.a.child_node(&node, 1)
|
||||
g.write('if (')
|
||||
g.gen_expr(g.a.child(&node, 0))
|
||||
g.writeln(') {')
|
||||
g.gen_if_expr_block(then_block)
|
||||
g.write('} else ')
|
||||
if node.children_count > 2 {
|
||||
else_node := g.a.child_node(&node, 2)
|
||||
if else_node.kind == .if_expr {
|
||||
g.gen_if_expr_else_if(*else_node)
|
||||
} else if else_node.kind == .block {
|
||||
g.writeln('{')
|
||||
g.gen_if_expr_block(else_node)
|
||||
g.writeln('}')
|
||||
}
|
||||
} else {
|
||||
g.writeln('{ _ifexpr = (typeof(_ifexpr)){0}; }')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
module c
|
||||
|
||||
import v3.types
|
||||
import v3.flat
|
||||
|
||||
fn (mut g FlatGen) emit_sum_type(name string) {
|
||||
variants := g.tc.sum_types[name]
|
||||
g.writeln('struct ${c_name(name)} {')
|
||||
g.writeln('\tint typ;')
|
||||
g.writeln('\tunion {')
|
||||
for v in variants {
|
||||
ct := g.tc.c_type(g.tc.parse_type(v))
|
||||
field := g.sum_field_name(v)
|
||||
g.writeln('\t\t${ct}* ${field};')
|
||||
}
|
||||
g.writeln('\t};')
|
||||
g.writeln('};')
|
||||
g.writeln('')
|
||||
}
|
||||
|
||||
fn (g &FlatGen) sum_type_contains_struct(sum_name string, struct_name string) bool {
|
||||
if sum_name in g.tc.sum_types {
|
||||
for v in g.tc.sum_types[sum_name] {
|
||||
if v == struct_name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (g &FlatGen) variant_references_sum(variant string, sum_name string) bool {
|
||||
_ = variant
|
||||
_ = sum_name
|
||||
return true
|
||||
}
|
||||
|
||||
fn (g &FlatGen) variant_refs_sum_inner(variant string, sum_name string, mut visited map[string]bool) bool {
|
||||
normalized_variant := g.normalize_variant_name(variant)
|
||||
if normalized_variant == sum_name
|
||||
|| normalized_variant.all_after_last('.') == sum_name.all_after_last('.') {
|
||||
return true
|
||||
}
|
||||
if normalized_variant in visited {
|
||||
return false
|
||||
}
|
||||
visited[normalized_variant] = true
|
||||
mut lookup := normalized_variant
|
||||
if lookup !in g.tc.structs && !lookup.contains('.') && sum_name.contains('.') {
|
||||
qualified := '${sum_name.all_before_last('.')}.${lookup}'
|
||||
if qualified in g.tc.structs {
|
||||
lookup = qualified
|
||||
}
|
||||
}
|
||||
if lookup !in g.tc.structs && !lookup.contains('.') {
|
||||
for struct_name, _ in g.tc.structs {
|
||||
if struct_name.all_after_last('.') == lookup {
|
||||
lookup = struct_name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if lookup in g.tc.structs {
|
||||
for f in g.tc.structs[lookup] {
|
||||
if g.type_references_sum(f.typ, sum_name, mut visited) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (g &FlatGen) normalize_variant_name(name string) string {
|
||||
_ = g
|
||||
mut res := name
|
||||
if res.starts_with('&') {
|
||||
res = res[1..]
|
||||
}
|
||||
if res.starts_with('ptr') && res.len > 3 {
|
||||
res = res[3..]
|
||||
}
|
||||
if res.contains('__') && !res.contains('.') {
|
||||
res = res.replace('__', '.')
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn (g &FlatGen) type_references_sum(typ types.Type, sum_name string, mut visited map[string]bool) bool {
|
||||
resolved_sum := g.resolve_sum_name(sum_name)
|
||||
clean := types.unwrap_pointer(typ)
|
||||
if clean is types.Struct && g.resolve_sum_name(clean.name) == resolved_sum {
|
||||
return true
|
||||
}
|
||||
if clean is types.SumType && g.resolve_sum_name(clean.name) == resolved_sum {
|
||||
return true
|
||||
}
|
||||
if clean is types.SumType {
|
||||
return true
|
||||
}
|
||||
if clean is types.Struct {
|
||||
if g.variant_refs_sum_inner(clean.name, resolved_sum, mut visited) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if clean is types.Array {
|
||||
return g.type_references_sum(clean.elem_type, resolved_sum, mut visited)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (g &FlatGen) resolve_sum_name(sum_name string) string {
|
||||
if sum_name in g.tc.sum_types {
|
||||
return sum_name
|
||||
}
|
||||
for name, _ in g.tc.sum_types {
|
||||
if name.all_after_last('.') == sum_name {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return sum_name
|
||||
}
|
||||
|
||||
fn (g &FlatGen) resolve_variant(sum_name string, variant string) string {
|
||||
resolved_sum := g.resolve_sum_name(sum_name)
|
||||
normalized_variant := g.normalize_variant_name(variant)
|
||||
if resolved_sum in g.tc.sum_types {
|
||||
for v in g.tc.sum_types[resolved_sum] {
|
||||
if v == normalized_variant {
|
||||
return normalized_variant
|
||||
}
|
||||
}
|
||||
for v in g.tc.sum_types[resolved_sum] {
|
||||
if v.all_after_last('.') == normalized_variant {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
return normalized_variant
|
||||
}
|
||||
|
||||
fn (g &FlatGen) sum_field_name(variant string) string {
|
||||
if variant.starts_with('&') {
|
||||
return g.sum_field_name(variant[1..])
|
||||
}
|
||||
if variant.starts_with('ptr') && variant.len > 3 && variant[3..].contains('.') {
|
||||
return g.sum_field_name(variant[3..])
|
||||
}
|
||||
if variant.starts_with('ptr') && variant.len > 3 && variant[3..].contains('__') {
|
||||
return g.sum_field_name(variant[3..].replace('__', '.'))
|
||||
}
|
||||
if variant.starts_with('[]') {
|
||||
return '_Array_${c_name(variant[2..])}'
|
||||
}
|
||||
if variant.starts_with('map[') {
|
||||
return '_Map_${c_name(variant[4..].replace(']', '_'))}'
|
||||
}
|
||||
return match variant {
|
||||
'int' { '_int' }
|
||||
'i8' { '_i8' }
|
||||
'i16' { '_i16' }
|
||||
'i64' { '_i64' }
|
||||
'u8', 'byte' { '_u8' }
|
||||
'u16' { '_u16' }
|
||||
'u32' { '_u32' }
|
||||
'u64' { '_u64' }
|
||||
'f32' { '_f32' }
|
||||
'f64' { '_f64' }
|
||||
'bool' { '_bool' }
|
||||
'string' { '_string' }
|
||||
else { c_name(variant) }
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) register_interface_strings() {
|
||||
for iface_name, methods in g.interfaces {
|
||||
cn := c_name(iface_name)
|
||||
for method in methods {
|
||||
g.intern_string('interface method ${cn}.${method} not implemented')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collect_interface_impls discovers, for every interface, the concrete struct
|
||||
// types that implement it (structural typing), and assigns each a stable 1-based
|
||||
// type id. The id is stored in the boxed interface value's `_typ` field and is
|
||||
// what the generated method-dispatch switch matches on.
|
||||
fn (mut g FlatGen) collect_interface_impls() {
|
||||
mut iface_names := []string{}
|
||||
for name, _ in g.interfaces {
|
||||
iface_names << name
|
||||
}
|
||||
iface_names.sort()
|
||||
mut struct_names := []string{}
|
||||
for name, _ in g.tc.structs {
|
||||
struct_names << name
|
||||
}
|
||||
struct_names.sort()
|
||||
for iface in iface_names {
|
||||
if c_name(iface) == 'IError' {
|
||||
continue
|
||||
}
|
||||
mut impls := []string{}
|
||||
for concrete in struct_names {
|
||||
if g.tc.named_type_implements_interface(concrete, iface) {
|
||||
impls << concrete
|
||||
}
|
||||
}
|
||||
g.iface_impls[iface] = impls
|
||||
for idx, concrete in impls {
|
||||
g.iface_type_ids['${iface}::${concrete}'] = idx + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iface_type_id returns the 1-based dispatch id assigned to `concrete` for
|
||||
// interface `iface`, or 0 if `concrete` does not implement `iface`.
|
||||
fn (g &FlatGen) iface_type_id(iface string, concrete string) int {
|
||||
return g.iface_type_ids['${iface}::${concrete}'] or { 0 }
|
||||
}
|
||||
|
||||
fn (g &FlatGen) is_interface_type_name(name string) bool {
|
||||
return name in g.interfaces || g.tc.qualify_name(name) in g.interfaces
|
||||
}
|
||||
|
||||
fn (g &FlatGen) has_ierror_interface() bool {
|
||||
for name, _ in g.interfaces {
|
||||
if c_name(name) == 'IError' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// interface_init_typ_id computes the `_typ` dispatch id for a boxed interface
|
||||
// literal by recovering the concrete type from its `_object` field.
|
||||
fn (g &FlatGen) interface_init_typ_id(node flat.Node) ?int {
|
||||
iface := if node.value in g.interfaces {
|
||||
node.value
|
||||
} else {
|
||||
g.tc.qualify_name(node.value)
|
||||
}
|
||||
for i in 0 .. node.children_count {
|
||||
field := g.a.child_node(&node, i)
|
||||
if field.kind == .field_init && field.value == '_object' && field.children_count > 0 {
|
||||
obj_type := g.tc.resolve_type(g.a.child(field, 0))
|
||||
concrete := types.unwrap_pointer(obj_type)
|
||||
id := g.iface_type_id(iface, concrete.name())
|
||||
if id != 0 {
|
||||
return id
|
||||
}
|
||||
return none
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// interface_method_stubs emits a dispatch function for every abstract interface
|
||||
// method: it switches on the boxed value's `_typ` and forwards to the concrete
|
||||
// implementation, passing `_object` as the receiver. Interfaces with no known
|
||||
// implementers (and the special builtin `IError`) fall back to a panic stub.
|
||||
fn (mut g FlatGen) interface_method_stubs() {
|
||||
for iface_name, methods in g.interfaces {
|
||||
cn := c_name(iface_name)
|
||||
for method in methods {
|
||||
g.gen_interface_dispatch(iface_name, cn, method)
|
||||
}
|
||||
}
|
||||
if g.interfaces.len > 0 {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) gen_interface_dispatch(iface_name string, cn string, method string) {
|
||||
sid := g.intern_string('interface method ${cn}.${method} not implemented')
|
||||
mname := '${iface_name}.${method}'
|
||||
impls := g.iface_impls[iface_name] or { []string{} }
|
||||
if cn == 'IError' {
|
||||
ret_ct := if method == 'code' { 'int' } else { 'string' }
|
||||
g.writeln('${ret_ct} ${cn}__${method}(${cn}* i) {')
|
||||
match method {
|
||||
'msg' {
|
||||
g.writeln('\treturn i->message;')
|
||||
}
|
||||
'code' {
|
||||
g.writeln('\treturn i->code;')
|
||||
}
|
||||
else {
|
||||
g.writeln('\tv_panic(_str_${sid});')
|
||||
g.writeln('\treturn (${ret_ct}){0};')
|
||||
}
|
||||
}
|
||||
|
||||
g.writeln('}')
|
||||
return
|
||||
}
|
||||
// Interface-declared method signatures store named params unreliably (a named
|
||||
// param like `node &ast.Node` can be split into two type-only params). The
|
||||
// concrete implementer's method is a real fn_decl with a correctly parsed
|
||||
// signature, so derive the dispatch parameter types from the first implementer
|
||||
// that has the method. The receiver convention is resolved per implementer.
|
||||
mut sig_key := ''
|
||||
for concrete in impls {
|
||||
ck := '${concrete}.${method}'
|
||||
if ck in g.tc.fn_param_types {
|
||||
sig_key = ck
|
||||
break
|
||||
}
|
||||
}
|
||||
ret_type := g.tc.fn_ret_types[mname] or {
|
||||
if sig_key.len > 0 {
|
||||
g.tc.fn_ret_types[sig_key] or { types.Type(types.void_) }
|
||||
} else {
|
||||
types.Type(types.void_)
|
||||
}
|
||||
}
|
||||
ret_ct := g.optional_type_name(ret_type)
|
||||
mut sig_params := if sig_key.len > 0 {
|
||||
g.tc.fn_param_types[sig_key] or { []types.Type{} }
|
||||
} else {
|
||||
[]types.Type{}
|
||||
}
|
||||
mut arg_names := []string{}
|
||||
g.write('${ret_ct} ${cn}__${method}(${cn}* i')
|
||||
for pi := 1; pi < sig_params.len; pi++ {
|
||||
pt := sig_params[pi]
|
||||
pct := if pt is types.OptionType || pt is types.ResultType {
|
||||
g.optional_type_name(pt)
|
||||
} else {
|
||||
g.tc.c_type(pt)
|
||||
}
|
||||
an := '_a${pi - 1}'
|
||||
arg_names << an
|
||||
g.write(', ${pct} ${an}')
|
||||
}
|
||||
g.writeln(') {')
|
||||
if impls.len > 0 {
|
||||
g.writeln('\tswitch (i->_typ) {')
|
||||
for concrete in impls {
|
||||
id := g.iface_type_id(iface_name, concrete)
|
||||
concrete_key := '${concrete}.${method}'
|
||||
if id == 0 || concrete_key !in g.tc.fn_param_types {
|
||||
continue
|
||||
}
|
||||
concrete_params := g.tc.fn_param_types[concrete_key] or { []types.Type{} }
|
||||
recv_is_ptr := concrete_params.len > 0 && concrete_params[0] is types.Pointer
|
||||
cct := g.tc.c_type(g.tc.parse_type(concrete))
|
||||
recv := if recv_is_ptr {
|
||||
'(${cct}*)i->_object'
|
||||
} else {
|
||||
'*(${cct}*)i->_object'
|
||||
}
|
||||
g.write('\t\tcase ${id}: ')
|
||||
mut call := '${c_name(concrete_key)}(${recv}'
|
||||
for an in arg_names {
|
||||
call += ', ${an}'
|
||||
}
|
||||
call += ')'
|
||||
if ret_ct == 'void' {
|
||||
g.writeln('${call}; return;')
|
||||
} else {
|
||||
g.writeln('return ${call};')
|
||||
}
|
||||
}
|
||||
g.writeln('\t\tdefault: break;')
|
||||
g.writeln('\t}')
|
||||
}
|
||||
g.writeln('\tv_panic(_str_${sid});')
|
||||
if ret_ct != 'void' {
|
||||
g.writeln('\treturn (${ret_ct}){0};')
|
||||
}
|
||||
g.writeln('}')
|
||||
}
|
||||
|
||||
fn (g &FlatGen) sum_type_index(sum_name string, variant string) int {
|
||||
if sum_name in g.tc.sum_types {
|
||||
for i, v in g.tc.sum_types[sum_name] {
|
||||
if v == variant {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
for i, v in g.tc.sum_types[sum_name] {
|
||||
if v.all_after_last('.') == variant {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
module c
|
||||
|
||||
import strings
|
||||
|
||||
const c_reserved_words = ['auto', 'break', 'case', 'char', 'const', 'continue', 'copy', 'default',
|
||||
'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
|
||||
'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
|
||||
'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']
|
||||
|
||||
fn c_name(name string) string {
|
||||
if name.starts_with('C.') {
|
||||
return name[2..]
|
||||
}
|
||||
if name == 'malloc' {
|
||||
return 'v_malloc'
|
||||
}
|
||||
// The V builtin `exit` wraps `C.exit`; both lower to the C symbol `exit`.
|
||||
// Rename the V function (and its call sites) to `v_exit` so its body's
|
||||
// `C.exit(code)` call resolves to libc `exit` instead of recursing forever.
|
||||
// `C.exit` itself is handled by the `C.` strip above, so it stays `exit`.
|
||||
if name == 'exit' {
|
||||
return 'v_exit'
|
||||
}
|
||||
if c_name_is_plain(name) {
|
||||
if name in c_reserved_words {
|
||||
return 'v_${name}'
|
||||
}
|
||||
return name
|
||||
}
|
||||
n := c_name_sanitize(name)
|
||||
if n in c_reserved_words {
|
||||
return 'v_${n}'
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
fn c_name_sanitize(name string) string {
|
||||
mut b := strings.new_builder(name.len + 8)
|
||||
mut i := 0
|
||||
for i < name.len {
|
||||
c := name[i]
|
||||
if c == `[` {
|
||||
if i + 1 < name.len && name[i + 1] == `]` {
|
||||
b.write_string('Array_')
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
b.write_u8(`_`)
|
||||
} else if c == `]` {
|
||||
i++
|
||||
continue
|
||||
} else if c == `.` {
|
||||
if i + 1 < name.len {
|
||||
next := name[i + 1]
|
||||
if next == `-` {
|
||||
b.write_string('__minus')
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if next == `+` {
|
||||
b.write_string('__plus')
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if i + 2 < name.len {
|
||||
op := name[i + 2]
|
||||
if next == `=` && op == `=` {
|
||||
b.write_string('__eq')
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
if next == `!` && op == `=` {
|
||||
b.write_string('__ne')
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
if next == `<` && op == `=` {
|
||||
b.write_string('__le')
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
if next == `>` && op == `=` {
|
||||
b.write_string('__ge')
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
}
|
||||
if next == `<` {
|
||||
b.write_string('__lt')
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if next == `>` {
|
||||
b.write_string('__gt')
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
}
|
||||
b.write_string('__')
|
||||
} else if c == `&` {
|
||||
b.write_string('ptr')
|
||||
} else if c == `,` || c == ` ` {
|
||||
b.write_u8(`_`)
|
||||
} else {
|
||||
b.write_u8(c)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return b.str()
|
||||
}
|
||||
|
||||
fn c_name_is_plain(name string) bool {
|
||||
for i in 0 .. name.len {
|
||||
c := name[i]
|
||||
if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || (c >= `0` && c <= `9`) || c == `_` {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn c_escape(s string) string {
|
||||
return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\t', '\\t').replace('\r',
|
||||
'\\r')
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
module c
|
||||
|
||||
fn test_c_name_sanitize_operator_overloads() {
|
||||
assert c_name('Point.<') == 'Point__lt'
|
||||
assert c_name('Point.<=') == 'Point__le'
|
||||
assert c_name('Point.>') == 'Point__gt'
|
||||
assert c_name('Point.>=') == 'Point__ge'
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,82 @@
|
||||
module c
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
fn (mut g FlatGen) gen_string_interp(node flat.Node) {
|
||||
n := node.children_count
|
||||
if n == 0 {
|
||||
sid := g.intern_string('')
|
||||
g.write('_str_${sid}')
|
||||
return
|
||||
}
|
||||
g.write('string_plus_many(${n}, (string[${n}]){')
|
||||
for i in 0 .. n {
|
||||
if i > 0 {
|
||||
g.write(', ')
|
||||
}
|
||||
child_id := g.a.child(&node, i)
|
||||
child := g.a.nodes[int(child_id)]
|
||||
if child.kind == .string_literal {
|
||||
sid := g.intern_string(child.value)
|
||||
g.write('_str_${sid}')
|
||||
} else if child.typ == 'string' {
|
||||
g.gen_expr(child_id)
|
||||
} else {
|
||||
typ := g.tc.resolve_type(child_id)
|
||||
if typ is types.String {
|
||||
g.gen_expr(child_id)
|
||||
} else if typ is types.Struct && typ.name == 'IError' {
|
||||
g.gen_expr(child_id)
|
||||
g.write('.message')
|
||||
} else if typ is types.Primitive {
|
||||
ct := g.tc.c_type(typ)
|
||||
g.write('${ct}_str(')
|
||||
g.gen_expr(child_id)
|
||||
g.write(')')
|
||||
} else if typ is types.ISize || typ is types.USize {
|
||||
g.write('${typ.name()}_str(')
|
||||
g.gen_expr(child_id)
|
||||
g.write(')')
|
||||
} else if typ is types.Struct {
|
||||
g.write('${c_name(typ.name)}__str(')
|
||||
g.gen_expr(child_id)
|
||||
g.write(')')
|
||||
} else if typ is types.SumType {
|
||||
g.write('${c_name(typ.name)}__str(')
|
||||
g.gen_expr(child_id)
|
||||
g.write(')')
|
||||
} else {
|
||||
g.write('int_str(')
|
||||
g.gen_expr(child_id)
|
||||
g.write(')')
|
||||
}
|
||||
}
|
||||
}
|
||||
g.write('})')
|
||||
}
|
||||
|
||||
fn (g &FlatGen) is_string_node(id flat.NodeId) bool {
|
||||
return g.tc.resolve_type(id) is types.String
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) string_literals() {
|
||||
for i, s in g.str_lits {
|
||||
escaped := s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\t',
|
||||
'\\t').replace('\r', '\\r')
|
||||
g.writeln('string _str_${i} = {"${escaped}", ${s.len}, 1};')
|
||||
}
|
||||
if g.str_lits.len > 0 {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) intern_string(s string) int {
|
||||
if s in g.str_lit_ids {
|
||||
return g.str_lit_ids[s]
|
||||
}
|
||||
id := g.str_lits.len
|
||||
g.str_lits << s
|
||||
g.str_lit_ids[s] = id
|
||||
return id
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,216 @@
|
||||
module c
|
||||
|
||||
import v3.flat
|
||||
import v3.types
|
||||
|
||||
fn (mut g FlatGen) optional_type_name(t types.Type) string {
|
||||
mut base_type := types.Type(types.void_)
|
||||
if t is types.OptionType {
|
||||
base_type = t.base_type
|
||||
} else if t is types.ResultType {
|
||||
base_type = t.base_type
|
||||
} else {
|
||||
return g.tc.c_type(t)
|
||||
}
|
||||
|
||||
if base_type is types.Void || base_type is types.Primitive || base_type is types.Enum {
|
||||
return 'Optional'
|
||||
}
|
||||
mut inner_ct := g.tc.c_type(base_type)
|
||||
if inner_ct.starts_with('fn_ptr:') {
|
||||
inner_ct = g.resolve_fn_ptr_type(inner_ct)
|
||||
}
|
||||
safe_name := inner_ct.replace('*', 'ptr').replace(' ', '_')
|
||||
opt_name := 'Optional_${safe_name}'
|
||||
g.needed_optional_types[opt_name] = inner_ct
|
||||
return opt_name
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) optional_value_ct(t types.Type) (string, types.Type) {
|
||||
if t is types.OptionType {
|
||||
if t.base_type is types.Void {
|
||||
return 'int', types.Type(types.int_)
|
||||
}
|
||||
return g.tc.c_type(t.base_type), t.base_type
|
||||
} else if t is types.ResultType {
|
||||
if t.base_type is types.Void {
|
||||
return 'int', types.Type(types.int_)
|
||||
}
|
||||
return g.tc.c_type(t.base_type), t.base_type
|
||||
}
|
||||
return 'int', types.Type(types.int_)
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) optional_typedefs() {
|
||||
for _, ret in g.tc.fn_ret_types {
|
||||
if ret is types.OptionType || ret is types.ResultType {
|
||||
g.optional_type_name(ret)
|
||||
}
|
||||
}
|
||||
mut wrote := false
|
||||
for opt_name, val_type in g.needed_optional_types {
|
||||
if g.emit_optional_typedef(opt_name, val_type) {
|
||||
wrote = true
|
||||
}
|
||||
}
|
||||
if wrote {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) emit_optional_typedef(opt_name string, val_type string) bool {
|
||||
if opt_name in g.emitted_optional_types {
|
||||
return false
|
||||
}
|
||||
err_field := if g.has_ierror_interface() { 'IError err; ' } else { '' }
|
||||
g.writeln('typedef struct { bool ok; ${err_field}${val_type} value; } ${opt_name};')
|
||||
g.emitted_optional_types[opt_name] = true
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) enum_decls() {
|
||||
mut cur_module := ''
|
||||
for node in g.a.nodes {
|
||||
match node.kind {
|
||||
.file {
|
||||
cur_module = ''
|
||||
}
|
||||
.module_decl {
|
||||
cur_module = node.value
|
||||
}
|
||||
.enum_decl {
|
||||
name := if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
|
||||
'${cur_module}.${node.value}'
|
||||
} else {
|
||||
node.value
|
||||
}
|
||||
cn := c_name(name)
|
||||
g.writeln('typedef enum {')
|
||||
is_flag := node.typ == 'flag'
|
||||
mut val := 0
|
||||
for i in 0 .. node.children_count {
|
||||
f := g.a.child_node(&node, i)
|
||||
if f.children_count > 0 {
|
||||
if enum_val := g.enum_field_expr_value(g.a.child(f, 0)) {
|
||||
val = enum_val
|
||||
}
|
||||
}
|
||||
if is_flag {
|
||||
g.writeln('\t${cn}__${f.value} = ${1 << val},')
|
||||
val++
|
||||
} else {
|
||||
g.writeln('\t${cn}__${f.value} = ${val},')
|
||||
val++
|
||||
}
|
||||
}
|
||||
g.writeln('} ${cn};')
|
||||
g.writeln('')
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (g &FlatGen) enum_field_expr_value(id flat.NodeId) ?int {
|
||||
if int(id) < 0 {
|
||||
return none
|
||||
}
|
||||
node := g.a.nodes[int(id)]
|
||||
match node.kind {
|
||||
.int_literal {
|
||||
return node.value.int()
|
||||
}
|
||||
.paren {
|
||||
if node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
return g.enum_field_expr_value(g.a.child(&node, 0))
|
||||
}
|
||||
.prefix {
|
||||
if node.children_count == 0 {
|
||||
return none
|
||||
}
|
||||
value := g.enum_field_expr_value(g.a.child(&node, 0))?
|
||||
return match node.op {
|
||||
.plus { value }
|
||||
.minus { -value }
|
||||
.bit_not { ~value }
|
||||
else { none }
|
||||
}
|
||||
}
|
||||
.infix {
|
||||
if node.children_count < 2 {
|
||||
return none
|
||||
}
|
||||
left := g.enum_field_expr_value(g.a.child(&node, 0))?
|
||||
right := g.enum_field_expr_value(g.a.child(&node, 1))?
|
||||
return match node.op {
|
||||
.plus {
|
||||
left + right
|
||||
}
|
||||
.minus {
|
||||
left - right
|
||||
}
|
||||
.mul {
|
||||
left * right
|
||||
}
|
||||
.div {
|
||||
if right == 0 {
|
||||
none
|
||||
} else {
|
||||
left / right
|
||||
}
|
||||
}
|
||||
.mod {
|
||||
if right == 0 {
|
||||
none
|
||||
} else {
|
||||
left % right
|
||||
}
|
||||
}
|
||||
.amp {
|
||||
left & right
|
||||
}
|
||||
.pipe {
|
||||
left | right
|
||||
}
|
||||
.xor {
|
||||
left ^ right
|
||||
}
|
||||
.left_shift {
|
||||
int(u64(left) << right)
|
||||
}
|
||||
.right_shift, .right_shift_unsigned {
|
||||
left >> right
|
||||
}
|
||||
else {
|
||||
none
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g FlatGen) type_alias_decls() {
|
||||
mut emitted := false
|
||||
for name, target in g.tc.type_aliases {
|
||||
if target.starts_with('fn_ptr:') || target.starts_with('C.') {
|
||||
continue
|
||||
}
|
||||
if g.has_builtins {
|
||||
continue
|
||||
}
|
||||
ct := g.tc.c_type(g.tc.parse_type(target))
|
||||
if ct == 'void' || ct == name {
|
||||
continue
|
||||
}
|
||||
g.writeln('typedef ${ct} ${c_name(name)};')
|
||||
emitted = true
|
||||
}
|
||||
if emitted {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user