Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+392
View File
@@ -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
+405
View File
@@ -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
}
}
}
+5
View File
@@ -0,0 +1,5 @@
module c
fn (mut g FlatGen) gen_fns_dispatch(_no_parallel bool) {
g.gen_fns()
}
+178
View File
@@ -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 {}
}
}
+410
View File
@@ -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}; }')
}
}
+388
View File
@@ -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
}
+126
View File
@@ -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')
}
+8
View File
@@ -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
+82
View File
@@ -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
+216
View File
@@ -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('')
}
}