translate-c file emission

This commit is contained in:
2026-06-24 22:53:20 +02:00
parent f6fc25a899
commit 4cb0ad7f25
7 changed files with 512 additions and 4 deletions
+81
View File
@@ -17,6 +17,7 @@ import "./compiler/source"
import "./compiler/symbol"
import "./compiler/target"
import "./compiler/token"
import "./compiler/translatec"
import "./compiler/types"
import "core:fmt"
import "core:mem"
@@ -5769,3 +5770,83 @@ native_enums_compile_and_run_across_packages :: proc(t: ^testing.T) {
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
translate_c_emits_native_bindings_and_round_trips :: proc(t: ^testing.T) {
result := cimport.init_result(context.allocator)
// type table: [0]=c_int, [1]=c_ulong, [2]=Record(Pair),
// [3]=Function(c_int)->c_int, [4]=Pointer->Function (callback)
append(&result.types, cimport.Type{kind = .C_Int, child = cimport.INVALID_TYPE})
append(&result.types, cimport.Type{kind = .C_Ulong, child = cimport.INVALID_TYPE})
append(&result.types, cimport.Type{kind = .Record, record = 0, child = cimport.INVALID_TYPE})
func_params := []cimport.Type_Id{cimport.Type_Id(0)}
append(&result.types, cimport.Type{kind = .Function, params = func_params, child = cimport.Type_Id(0)})
append(&result.types, cimport.Type{kind = .Pointer, child = cimport.Type_Id(3)})
// record 0: Pair { left c_int; right c_int }
pair_fields: [dynamic]cimport.Field
append(&pair_fields, cimport.Field{name = "left", type = cimport.Type_Id(0)})
append(&pair_fields, cimport.Field{name = "right", type = cimport.Type_Id(0)})
append(&result.records, cimport.Record{name = "Pair", fields = pair_fields, kind = .Struct, complete = true})
// record 1: union Choice -> commented (no native spelling)
choice_fields: [dynamic]cimport.Field
append(&choice_fields, cimport.Field{name = "tag", type = cimport.Type_Id(0)})
append(&result.records, cimport.Record{name = "Choice", fields = choice_fields, kind = .Union, complete = true})
// typedef aliases: a scalar and a callback function pointer
append(&result.aliases, cimport.Alias{name = "Size", type = cimport.Type_Id(1)})
append(&result.aliases, cimport.Alias{name = "Mapper", type = cimport.Type_Id(4)})
add_params := []cimport.Type_Id{cimport.Type_Id(0), cimport.Type_Id(0)}
append(&result.functions, cimport.Function{name = "imported_add", params = add_params, result = cimport.Type_Id(0)})
append(&result.macros, cimport.Macro_Constant{
name = "MAX_LEN",
type = cimport.Type_Id(0),
value = {kind = .Integer, type = cimport.Type_Id(0), integer = 256},
})
// external variable -> commented (no native spelling)
append(&result.variables, cimport.Variable{name = "some_global", type = cimport.Type_Id(0), mutable = true})
result.available = true
output := translatec.emit(&result, "test.h")
defer delete(output)
defer {
delete(result.types)
delete(result.records)
delete(result.aliases)
delete(result.functions)
delete(result.variables)
delete(result.macros)
delete(pair_fields)
delete(choice_fields)
}
testing.expect(t, strings.contains(output, "Pair :: c_struct {"))
testing.expect(t, strings.contains(output, "\tleft c_int"))
testing.expect(t, strings.contains(output, "\tright c_int"))
testing.expect(t, strings.contains(output, "Size :: alias c_ulong"))
testing.expect(t, strings.contains(output, "Mapper :: alias ?*c_func(arg0 c_int) c_int"))
testing.expect(t, strings.contains(output, "imported_add :: c_func(arg0 c_int, arg1 c_int) c_int"))
testing.expect(t, strings.contains(output, "MAX_LEN c_int :: 256"))
testing.expect(t, strings.contains(output, "# unsupported in bindings: C union 'Choice'"))
testing.expect(t, strings.contains(output, "# unsupported in bindings: external variable 'some_global'"))
// Round-trip: the emitted source must lex + parse with zero diagnostics.
// This guards render_type against drift from loader.translate_c_type and
// exercises the new `alias` declaration syntax.
source_file := source.Source{path = "bindings.bro", text = output}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&module)
testing.expect_value(t, len(diagnostics.items), 0)
}